<?php
/**
 * Prepares response payload for the PowerWidget main view endpoint.
 *
 * @package Ilabs\Firevert\Modules\PowerWidget\Api
 */

declare( strict_types=1 );

namespace Ilabs\Firevert\Modules\PowerWidget\Api;

use Ilabs\Firevert\Core\Services\Helper\Integration\OmnibusHelper;
use Ilabs\Firevert\Modules\PowerWidget\Services\Type\PositionType;
use WC_Product;
use WP_Post;

/**
 * Class RenderResponsePreparer
 *
 * Generates the structured data payload returned by the
 * /firevert/v1/power-widget/main endpoint.
 */
class ResponsePreparer {
	/**
	 * Prepares the final frontend payload based on the widget data.
	 *
	 * @param WP_Post $post Widget post instance.
	 * @param array   $data PowerWidget data.
	 * @param array   $products_data PowerWidget products data.
	 *
	 * @return array<string, mixed>  Final response data structure for frontend rendering.
	 */
	public static function prepare_main_response( WP_Post $post, array $data, array $products_data ): array {
		$products_response = self::prepare_products_response( $products_data );

		return array(
			'el'      => '#power-widget-' . $post->ID,
			'config'  => array(
				'type'                => $data['firevert_power_widget_type'] ?? 'text',
				'visibleForAdminOnly' => $data['firevert_power_widget_visible_admin_only'] ?? false,
				'isAdmin'             => current_user_can( 'manage_options' ),
				'showOnDesktop'       => $data['firevert_power_widget_display_desktop'] ?? true,
				'showOnMobile'        => $data['firevert_power_widget_display_mobile'] ?? true,
				'expandOnMobile'      => $data['firevert_power_widget_expand_mobile'] ?? true,
				'position'            => $data['firevert_power_widget_display_position'] ?? PositionType::BOTTOM_RIGHT,
				'startAt'             => $data['firevert_power_widget_display_start'] ?? '',
				'endAt'               => $data['firevert_power_widget_display_end'] ?? '',
				'showTimer'           => $data['firevert_power_widget_display_timer'] ?? false,
			),
			'theme'   => array(
				'accentColor'      => $data['firevert_power_widget_primary_color'] ?? '#9900ff',
				'backgroundColor'  => $data['firevert_power_widget_bg_color'] ?? '#ffffff',
				'textColor'        => $data['firevert_power_widget_primary_text_color'] ?? '#000000',
				'counterTextColor' => $data['firevert_power_widget_counter_text'] ?? '#ffffff',
				'counterBgColor'   => $data['firevert_power_widget_counter_bg'] ?? '#000000',
				'shadow'           => $data['firevert_power_widget_shadow'] ?? false,
				'borderRadius'     => $data['firevert_power_widget_border_radius'] ?? '0',
			),
			'content' => array(
				'bar'         => array(
					'timerText'  => $data['firevert_power_widget_timer_label'] ?? __( 'Until end', 'firevert' ),
					'timerImage' => $data['firevert_power_widget_image_url'] ?? '',
					'promoText'  => $data['firevert_power_widget_text'] ?? '',
				),
				'powerwidget' => array(
					'accordion'  => array(
						'openText'  => $data['firevert_power_widget_toggle_open'] ?? __( 'Check promotions', 'firevert' ),
						'closeText' => $data['firevert_power_widget_toggle_close'] ?? __( 'Collapse', 'firevert' ),
					),
					'tabs'       => array(
						'productsTab' => array(
							'name'       => __( 'Products', 'firevert' ),
							'products'   => $products_response['data'],
							'pagination' => $products_response['pagination'],
						),
					),
					'bottomText' => $data['firevert_power_widget_bottom_text'] ?? __( 'See all', 'firevert' ),
					'button'     => array(
						'show' => $data['firevert_power_widget_products_show_button'] ?? false,
						'text' => $data['firevert_power_widget_products_button_text'] ?? '',
						'url'  => $data['firevert_power_widget_products_button_url'] ?? '',
					),
				),
			),
		);
	}

	/**
	 * Prepares the final frontend payload for products.
	 *
	 * @param array $products_data PowerWidget products data.
	 *
	 * @return array<string, mixed>  Final response data structure for frontend rendering.
	 */
	public static function prepare_products_response( array $products_data ): array {
		return array(
			'data'       => array_map(
				static function ( WC_Product $product ): array {
					$price_data = self::prepare_price_data( $product );
					$currency   = $price_data['currency'];
					$omnibus    = self::prepare_omnibus_string( $product, $price_data['price_current_raw'], $currency );

					return array(
						'name'      => $product->get_name(),
						'image'     => wp_get_attachment_image_url( $product->get_image_id(), 'woocommerce_gallery_thumbnail' ),
						'currency'  => $currency,
						'price'     => $price_data['price'],
						'salePrice' => $price_data['promo_price'],
						'omnibus'   => $omnibus,
						'link'      => get_permalink( $product->get_id() ),
					);
				},
				$products_data['products']
			),
			'pagination' => array(
				'currentPage' => $products_data['current_page'],
				'totalPages'  => $products_data['total_pages'],
				'hasMore'     => $products_data['has_more'],
			),
		);
	}

	/**
	 * Prepares the final Omnibus string for one product.
	 *
	 * Returns a single string like: "Lowest price from the last 30 days: 12,99 zł",
	 * or null when Omnibus shouldn't be displayed.
	 *
	 * @param WC_Product $product   WC_Product instance.
	 * @param float      $displayed Current price to display (incl/excl VAT as per shop).
	 * @param string     $currency  Currency symbol or code to append (e.g. "zł", "EUR").
	 *
	 * @return string|null Final Omnibus string or null.
	 */
	private static function prepare_omnibus_string( WC_Product $product, float $displayed, string $currency ): ?string {
		$omnibus_raw  = OmnibusHelper::get_lowest_price( $product->get_id() );
		$omnibus_days = OmnibusHelper::get_lowest_price_days_ago( $product->get_id() );

		if ( null === $omnibus_raw || null === $omnibus_days ) {
			return null;
		}

		$omnibus_display = (float) wc_get_price_to_display(
			$product,
			array( 'price' => $omnibus_raw )
		);

		$epsilon = 0.00001;
		if ( $displayed <= ( $omnibus_display + $epsilon ) ) {
			return null;
		}

		$omnibus_formatted = number_format(
			$omnibus_display,
			2,
			wc_get_price_decimal_separator(),
			''
		);

		return sprintf(
		/* translators: 1: number of days; 2: price (localized, two decimals, no thousands); 3: currency symbol or code. */
			__( 'Lowest price in %1$d days: %2$s %3$s', 'firevert' ),
			$omnibus_days,
			$omnibus_formatted,
			$currency
		);
	}

	/**
	 * Prepares raw prices without HTML: regular/current/sale in the shop's tax display mode.
	 *
	 * @param WC_Product $product WooCommerce product.
	 *
	 * @return array<string, mixed> 'currency','price_regular_raw','price_current_raw','price','promo_price'
	 */
	private static function prepare_price_data( WC_Product $product ): array {
		$currency     = get_woocommerce_currency();
		$currency_sym = ( 'PLN' !== $currency ) ? $currency : 'zł';

		$current_display = (float) wc_get_price_to_display( $product );
		$regular_raw     = $product->get_regular_price();

		$regular_display = ( '' !== $regular_raw && null !== $regular_raw )
			? (float) wc_get_price_to_display( $product, array( 'price' => $regular_raw ) )
			: $current_display;

		$sale_display = self::get_sale_price_to_display( $product );

		$promo_formatted = null;
		if ( ( $sale_display < $regular_display ) && null !== $sale_display && ! self::floats_equal( $sale_display, $regular_display ) ) {
			$promo_formatted = self::format_price_string( $sale_display );
		}

		return array(
			'currency'          => $currency_sym,
			'price_regular_raw' => $regular_display,
			'price_current_raw' => $current_display,
			'price'             => self::format_price_string( $regular_display ),
			'promo_price'       => $promo_formatted,
		);
	}

	/**
	 * Returns sale price converted to display price (incl/excl VAT as per shop setting), or null.
	 *
	 * @param WC_Product $product WooCommerce product.
	 *
	 * @return float|null
	 */
	private static function get_sale_price_to_display( WC_Product $product ): ?float {
		$sale = $product->get_sale_price();

		if ( '' === $sale || null === $sale ) {
			return null;
		}

		if ( true !== $product->is_on_sale() ) {
			return null;
		}

		$display = wc_get_price_to_display(
			$product,
			array( 'price' => $sale )
		);

		return (float) $display;
	}

	/**
	 * Formats a numeric price as string with exactly 2 decimals and the shop's decimal separator (no thousands).
	 *
	 * @param float|int|string $value Price value.
	 *
	 * @return string
	 */
	private static function format_price_string( $value ): string {
		return number_format(
			(float) $value,
			2,
			wc_get_price_decimal_separator(),
			''
		);
	}

	/**
	 * Compares two floats with a small tolerance.
	 *
	 * @param float $a First.
	 * @param float $b Second.
	 *
	 * @return bool
	 */
	private static function floats_equal( float $a, float $b ): bool {
		return abs( $a - $b ) < 0.00001;
	}
}
