<?php
/**
 * PowerWidgetRepository class.
 *
 * Provides access to PowerWidget CPT instances.
 *
 * @package Ilabs\Firevert\Modules\PowerWidget\Services\Repository
 */

declare( strict_types=1 );

namespace Ilabs\Firevert\Modules\PowerWidget\Services\Repository;

use Ilabs\Firevert\Core\Repository\Cache\CacheRepository;
use Ilabs\Firevert\Core\Repository\AbstractWidgetRepository;
use Ilabs\Firevert\Modules\PowerWidget\Api\ResponsePreparer;
use Ilabs\Firevert\Modules\PowerWidget\Services\Form\ProductsFormHandler;
use Ilabs\Firevert\Modules\PowerWidget\Services\Type\ModuleType;
use WC_Product;
use WP_Post;
use Ilabs\Firevert\Modules\PowerWidget\Services\Form\AppearanceFormHandler;
use Ilabs\Firevert\Modules\PowerWidget\Services\Form\SettingsFormHandler;

/**
 * Class PowerWidgetRepository
 */
final class PowerWidgetRepository extends AbstractWidgetRepository {

	public const MAX_PRODUCTS_LIMIT_PER_PAGE       = 10;
	public const POWER_WIDGET_PRODUCTS_CACHE_GROUP = 'power_widget_products';

	/**
	 * Returns the custom post type used by the widget.
	 *
	 * This defines the post_type for queries and creation.
	 *
	 * @return string Widget custom post type name.
	 */
	protected function get_post_type(): string {
		return ModuleType::MODULE_CPT;
	}

	/**
	 * Returns the default post title used when creating the widget.
	 *
	 * Used when no PromoTimeWidget instance exists.
	 *
	 * @return string Default title for the widget post.
	 */
	protected function get_default_post_title(): string {
		return 'Main Power Widget';
	}

	/**
	 * Returns the prepared payload for the main PowerWidget API response.
	 *
	 * Used by GET /firevert/v1/power-widget/main
	 *
	 * @param WP_Post     $post Widget post instance.
	 * @param string|null $lang Passed lang value ('pl', 'en', 'fr' format).
	 *
	 * @return array<string, mixed> Payload data for frontend consumption.
	 */
	public function get_main_widget_payload( WP_Post $post, ?string $lang = null ): array {
		$data           = $this->get_widget_data( $post, $lang );
		$product_result = $this->get_filtered_products( $post->ID, $data, 1, self::MAX_PRODUCTS_LIMIT_PER_PAGE );

		return ResponsePreparer::prepare_main_response(
			$post,
			$data,
			$product_result,
		);
	}

	/**
	 * Returns the prepared payload for the products PowerWidget API response.
	 *
	 * Used by GET /firevert/v1/power-widget/id/products
	 *
	 * @param WP_Post $post Widget post instance.
	 * @param int     $page Page number.
	 *
	 * @return array<string, mixed> Payload data for frontend consumption.
	 */
	public function get_products_widget_payload( WP_Post $post, int $page ): array {
		$products_handler = new ProductsFormHandler( $post );
		$products_data    = $this->extract_fields( $post, $products_handler );
		$product_result   = $this->get_filtered_products(
			$post->ID,
			$products_data,
			$page,
			self::MAX_PRODUCTS_LIMIT_PER_PAGE
		);

		return ResponsePreparer::prepare_products_response( $product_result );
	}

	/**
	 * Collects and merges all widget settings from Settings, Appearance and Products.
	 *
	 * @param WP_Post     $post PowerWidget CPT post.
	 * @param string|null $lang Passed lang value ('pl', 'en', 'fr' format).
	 *
	 * @return array<string, mixed> Combined widget settings array.
	 */
	public function get_widget_data( WP_Post $post, ?string $lang = null ): array {
		$settings_handler   = new SettingsFormHandler( $post );
		$appearance_handler = new AppearanceFormHandler( $post );
		$products_handler   = new ProductsFormHandler( $post );

		$settings_data   = $this->extract_fields( $post, $settings_handler, $lang );
		$appearance_data = $this->extract_fields( $post, $appearance_handler, $lang );
		$products_data   = $this->extract_fields( $post, $products_handler, $lang );

		$image_id = (int) ( $appearance_data['firevert_power_widget_image'] ?? 0 );

		$appearance_data['firevert_power_widget_image_id']  = $image_id;
		$appearance_data['firevert_power_widget_image_url'] = $image_id
			? wp_get_attachment_url( $image_id )
			: '';

		return array_merge(
			$settings_data,
			$appearance_data,
			$products_data
		);
	}

	/**
	 * Returns a list of WooCommerce products filtered by PowerWidget settings.
	 *
	 * @param int   $post_id PowerWidget CPT post ID.
	 * @param array $data PowerWidget CPT data.
	 * @param int   $paged Current page number (1-based).
	 * @param int   $limit Number of products per page.
	 *
	 * @return array<int, array<string, mixed>> List of product data.
	 */
	public function get_filtered_products( int $post_id, array $data, int $paged, int $limit ): array {
		if ( empty( $data['firevert_power_widget_products_enabled'] ) ) {
			return array(
				'products'     => array(),
				'current_page' => $paged,
				'total_pages'  => 0,
				'has_more'     => false,
			);
		}

		$include      = array_map( 'absint', $data['firevert_power_widget_products_selected'] ?? array() );
		$exclude      = array_map( 'absint', $data['firevert_power_widget_products_excluded'] ?? array() );
		$include_cats = array_map( 'absint', $data['firevert_power_widget_products_categories'] ?? array() );
		$exclude_cats = array_diff(
			array_map( 'absint', $data['firevert_power_widget_products_excluded_categories'] ?? array() ),
			$include_cats
		);

		$selection_mode = $data['firevert_power_widget_products_selection_mode'] ?? 'sale';

		$cache_key = 'pw_filtered_products_' . md5(
			wp_json_encode(
				array(
					'post_id'        => $post_id,
					'paged'          => $paged,
					'limit'          => $limit,
					'include'        => $include,
					'exclude'        => $exclude, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude
					'include_cats'   => $include_cats,
					'exclude_cats'   => $exclude_cats,
					'selection_mode' => $selection_mode,
				)
			)
		);

		/**
		 * Get container DI.
		 *
		 * @var CacheRepository $cache
		 */
		$cache  = firevert_container()->get( 'cache_repository' );
		$cached = $cache->get( $cache_key );
		if ( false !== $cached ) {
			return $cached;
		}

		$args = array(
			'post_type'      => 'product',
			'post_status'    => 'publish',
			'posts_per_page' => $limit,
			'paged'          => $paged,
			'post__not_in'   => $exclude, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in
			'tax_query'      => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
			'meta_query'     => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
		);

		if ( 'selected' === $selection_mode ) {
			if ( empty( $include ) ) {
				return array(
					'products'     => array(),
					'current_page' => $paged,
					'total_pages'  => 0,
					'has_more'     => false,
				);
			}
			$args['post__in'] = $include;
			$args['orderby']  = 'post__in';
		} elseif ( 'sale' === $selection_mode ) {
			$args['meta_query'][] = array(
				'key'     => '_sale_price',
				'value'   => 0,
				'compare' => '>',
				'type'    => 'NUMERIC',
			);
		}

		if ( ! empty( $include_cats ) || ! empty( $exclude_cats ) ) {
			$tax_query = array();

			if ( ! empty( $include_cats ) ) {
				$tax_query[] = array(
					'taxonomy' => 'product_cat',
					'field'    => 'term_id',
					'terms'    => $include_cats,
					'operator' => 'IN',
				);
			}

			if ( ! empty( $exclude_cats ) ) {
				$tax_query[] = array(
					'taxonomy' => 'product_cat',
					'field'    => 'term_id',
					'terms'    => $exclude_cats,
					'operator' => 'NOT IN',
				);
			}

			if ( count( $tax_query ) > 1 ) {
				$tax_query['relation'] = 'AND';
			}

			// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
			$args['tax_query'] = $tax_query;
		}

		$query    = new \WP_Query( $args );
		$products = array();

		foreach ( $query->posts as $product_post ) {
			$product = wc_get_product( $product_post );

			if ( $product instanceof WC_Product ) {
				$products[] = $product;
			}
		}

		$result = array(
			'products'     => $products,
			'current_page' => $paged,
			'total_pages'  => (int) $query->max_num_pages,
			'has_more'     => ( $paged < (int) $query->max_num_pages ),
		);

		$cache->set( $cache_key, $result, HOUR_IN_SECONDS, self::POWER_WIDGET_PRODUCTS_CACHE_GROUP );

		return $result;
	}
}
