<?php

namespace CelerSearch\Integrations\WooCommerce;

defined( 'ABSPATH' ) || exit;

use CelerSearch\DataTransfer\IndexResponses\SearchResponse;
use CelerSearch\Factories\IndexFactory;
use CelerSearch\Indices\BaseIndex;
use CelerSearch\Interfaces\IRegistrable;
use CelerSearch\Repositories\IndexRepository;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST API endpoint for WooCommerce shop filtering
 */
class ShopFilterRestApi implements IRegistrable {

	const NAMESPACE = 'celersearch/v1';

	/**
	 * Register hooks
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'rest_api_init', [ $this, 'register_routes' ] );
	}

	/**
	 * Register REST API routes
	 *
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route( self::NAMESPACE, '/shop-filter', [
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => [ $this, 'handle_filter' ],
			'permission_callback' => '__return_true',
			'args'                => [
				'q'        => [
					'required'          => false,
					'type'              => 'string',
					'default'           => '',
					'sanitize_callback' => 'sanitize_text_field',
				],
				'filters'  => [
					'required'          => false,
					'type'              => 'string',
					'default'           => '{}',
					'sanitize_callback' => 'sanitize_text_field',
				],
				'sort'     => [
					'required'          => false,
					'type'              => 'string',
					'default'           => '',
					'sanitize_callback' => 'sanitize_text_field',
				],
				'page'     => [
					'required'          => false,
					'type'              => 'integer',
					'default'           => 1,
					'sanitize_callback' => 'absint',
				],
				'per_page' => [
					'required'          => false,
					'type'              => 'integer',
					'default'           => 0,
					'sanitize_callback' => 'absint',
				],
				'index_id' => [
					'required'          => false,
					'type'              => 'integer',
					'default'           => 0,
					'sanitize_callback' => 'absint',
				],
			],
		] );
	}

	/**
	 * Handle shop filter request
	 *
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function handle_filter( WP_REST_Request $request ) {
		$query    = $request->get_param( 'q' );
		$filters  = json_decode( $request->get_param( 'filters' ), true ) ?: [];
		$sort     = $request->get_param( 'sort' );
		$page     = max( 1, $request->get_param( 'page' ) );
		$per_page = $request->get_param( 'per_page' );
		$index_id = $request->get_param( 'index_id' );

		// Determine per_page from WC settings if not provided
		if ( empty( $per_page ) && function_exists( 'wc_get_default_products_per_row' ) ) {
			$columns  = wc_get_default_products_per_row();
			$rows     = absint( get_option( 'woocommerce_catalog_rows', 4 ) );
			$per_page = $columns * $rows;
		}
		if ( empty( $per_page ) ) {
			$per_page = (int) get_option( 'posts_per_page', 12 );
		}

		// Resolve index
		if ( empty( $index_id ) ) {
			$index_id = $this->get_shop_browse_index_id();
		}

		if ( empty( $index_id ) ) {
			return new WP_Error(
				'no_index',
				__( 'No search index configured for shop.', 'celersearch' ),
				[ 'status' => 400 ]
			);
		}

		try {
			$index = IndexFactory::create( $index_id );

			// Build search params
			$search_filters = $this->build_filters( $filters );

			// Always add status = publish
			$search_filters[] = [
				'field'    => 'status',
				'operator' => '=',
				'value'    => 'publish',
			];

			// Build facets list from index filterable attributes
			$facet_attributes = $this->get_facet_attributes( $index );

			$params = [
				'limit'  => $per_page,
				'offset' => ( $page - 1 ) * $per_page,
				'facets' => $facet_attributes,
			];

			if ( ! empty( $search_filters ) ) {
				$params['filters'] = $search_filters;
			}

			// Sort
			if ( ! empty( $sort ) ) {
				$sort_rules = $this->resolve_sort( $sort );
				if ( ! empty( $sort_rules ) ) {
					$params['sort'] = $sort_rules;
				}
			}

			// Perform search
			$response = $index->search( $query, $params );

			if ( $response->is_error_response() ) {
				return new WP_Error(
					'search_error',
					__( 'Search failed.', 'celersearch' ),
					[ 'status' => 500 ]
				);
			}

			// Render HTML using WC templates
			$html = $this->render_products( $response->get_hits() );

			// Build facets response
			$facets = $response->get_facet_distribution();

			// Price range from facet stats
			$facet_stats = $response->get_facet_stats();
			$price_range = [
				'min' => $facet_stats['price']['min'] ?? 0,
				'max' => $facet_stats['price']['max'] ?? 0,
			];

			$total       = $response->get_total_hits();
			$total_pages = $per_page > 0 ? (int) ceil( $total / $per_page ) : 1;

			return new WP_REST_Response( [
				'html'        => $html,
				'facets'      => $facets,
				'price_range' => $price_range,
				'total'       => $total,
				'pages'       => $total_pages,
				'page'        => $page,
			] );

		} catch ( \Exception $e ) {
			return new WP_Error(
				'search_exception',
				$e->getMessage(),
				[ 'status' => 500 ]
			);
		}
	}

	/**
	 * Get the index ID configured for the woocommerce_shop_browse area
	 *
	 * @return int
	 */
	private function get_shop_browse_index_id(): int {
		$settings = get_option( 'celersearch_settings', [] );
		$areas    = $settings['search_areas'] ?? [];

		foreach ( $areas as $area ) {
			if ( ( $area['type'] ?? '' ) === 'woocommerce_shop_browse' && ! empty( $area['enabled'] ) ) {
				return (int) ( $area['index_id'] ?? 0 );
			}
		}

		// Fallback: try woocommerce_product_search area
		foreach ( $areas as $area ) {
			if ( ( $area['type'] ?? '' ) === 'woocommerce_product_search' && ! empty( $area['enabled'] ) ) {
				return (int) ( $area['index_id'] ?? 0 );
			}
		}

		return (int) ( $settings['default_index_id'] ?? 0 );
	}

	/**
	 * Build normalized filter conditions from the JSON filters param
	 *
	 * @param array $filters
	 *
	 * @return array
	 */
	private function build_filters( array $filters ): array {
		$conditions = [];

		// Price filter
		if ( isset( $filters['price'] ) ) {
			if ( isset( $filters['price']['min'] ) && $filters['price']['min'] > 0 ) {
				$conditions[] = [
					'field'    => 'price',
					'operator' => '>=',
					'value'    => (float) $filters['price']['min'],
				];
			}
			if ( isset( $filters['price']['max'] ) && $filters['price']['max'] > 0 ) {
				$conditions[] = [
					'field'    => 'price',
					'operator' => '<=',
					'value'    => (float) $filters['price']['max'],
				];
			}
		}

		// In stock filter
		if ( isset( $filters['in_stock'] ) && $filters['in_stock'] === true ) {
			$conditions[] = [
				'field'    => 'in_stock',
				'operator' => '=',
				'value'    => true,
			];
		}

		// Rating filter
		if ( isset( $filters['ratings.average_rating'] ) && is_array( $filters['ratings.average_rating'] ) ) {
			$ratings = array_map( 'intval', $filters['ratings.average_rating'] );
			$ratings = array_filter( $ratings, fn( $r ) => $r >= 1 && $r <= 5 );
			if ( ! empty( $ratings ) ) {
				$conditions[] = [
					'field'    => 'ratings.average_rating',
					'operator' => 'IN',
					'value'    => $ratings,
				];
			}
		}

		// Taxonomy filters (taxonomies.product_cat, taxonomies.pa_color, etc.)
		foreach ( $filters as $key => $value ) {
			if ( strpos( $key, 'taxonomies.' ) !== 0 ) {
				continue;
			}
			if ( ! is_array( $value ) || empty( $value ) ) {
				continue;
			}

			$conditions[] = [
				'field'    => $key,
				'operator' => count( $value ) === 1 ? '=' : 'IN',
				'value'    => count( $value ) === 1 ? $value[0] : $value,
			];
		}

		return $conditions;
	}

	/**
	 * Get facet attributes for the search request
	 *
	 * @param BaseIndex $index
	 *
	 * @return array
	 */
	private function get_facet_attributes( BaseIndex $index ): array {
		$settings    = $index->get_settings();
		$filterable  = $settings->get_filterable_attributes();
		$facets      = [];

		// Include known facet-worthy attributes
		$wanted = [
			'taxonomies',
			'in_stock',
			'ratings.average_rating',
			'price',
		];

		foreach ( $filterable as $attr ) {
			// Include taxonomies (covers product_cat, product_tag, pa_*)
			if ( in_array( $attr, $wanted, true ) ) {
				$facets[] = $attr;
			}
			// Include any explicit taxonomy attributes
			if ( strpos( $attr, 'taxonomies.' ) === 0 ) {
				$facets[] = $attr;
			}
		}

		// Ensure we always have price for stats
		if ( ! in_array( 'price', $facets, true ) ) {
			$facets[] = 'price';
		}

		return array_unique( $facets );
	}

	/**
	 * Resolve sort string to MeiliSearch sort rules
	 *
	 * @param string $sort
	 *
	 * @return array
	 */
	private function resolve_sort( string $sort ): array {
		$sort_map = [
			'popularity' => [ 'total_sales:desc' ],
			'rating'     => [ 'ratings.average_rating:desc' ],
			'date'       => [ 'post_date:desc' ],
			'price'      => [ 'price:asc' ],
			'price-desc' => [ 'price:desc' ],
		];

		return $sort_map[ $sort ] ?? [];
	}

	/**
	 * Render product HTML using WooCommerce templates
	 *
	 * @param array $hits
	 *
	 * @return string
	 */
	private function render_products( array $hits ): string {
		if ( empty( $hits ) || ! function_exists( 'wc_get_product' ) ) {
			return '';
		}

		// Collect post IDs preserving MeiliSearch order
		$post_ids = [];
		foreach ( $hits as $hit ) {
			$post_id = $hit['post_id'] ?? $hit['id'] ?? null;
			if ( $post_id ) {
				$post_ids[] = (int) $post_id;
			}
		}

		if ( empty( $post_ids ) ) {
			return '';
		}

		// Warm the post cache
		_prime_post_caches( $post_ids, true, true );

		ob_start();

		wc_set_loop_prop( 'total', count( $post_ids ) );
		wc_set_loop_prop( 'per_page', count( $post_ids ) );
		wc_set_loop_prop( 'current_page', 1 );

		foreach ( $post_ids as $post_id ) {
			$post = get_post( $post_id );
			if ( ! $post ) {
				continue;
			}

			$GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
			setup_postdata( $post );

			wc_get_template_part( 'content', 'product' );
		}

		wp_reset_postdata();

		return ob_get_clean();
	}
}
