<?php

namespace CelerSearch\Search;

defined( 'ABSPATH' ) || exit;

use CelerSearch\DataTransfer\IndexConfig;
use CelerSearch\Factories\IndexFactory;
use CelerSearch\Indices\BaseIndex;
use CelerSearch\Interfaces\IRegistrable;
use CelerSearch\Repositories\IndexRepository;
use CelerSearch\Utilities\Logger;

/**
 * Order Query Integration
 *
 * Handles search interception for WooCommerce orders in admin.
 * Since WooCommerce HPOS uses custom tables and not WP_Query,
 * the standard pre_get_posts hook won't fire. This class hooks
 * into the HPOS order list table query args to intercept searches.
 */
class OrderQueryIntegration implements IRegistrable {

	/**
	 * Register hooks
	 *
	 * @return void
	 */
	public function register(): void {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return;
		}

		// Hook into HPOS order query - this is the key hook for search interception
		add_filter( 'woocommerce_order_list_table_prepare_items_query_args', [ $this, 'maybe_intercept_order_query' ], 10, 1 );
	}

	/**
	 * Maybe intercept order query to use CelerSearch
	 *
	 * @param array $query_args The WooCommerce order query args.
	 *
	 * @return array Modified query args.
	 */
	public function maybe_intercept_order_query( array $query_args ): array {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search parameter
		$search_term = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';

		if ( empty( $search_term ) ) {
			return $query_args;
		}

		// Check if CelerSearch should handle this query
		if ( ! $this->should_intercept() ) {
			Logger::channel( Logger::CHANNEL_QUERIES )->debug( 'Order query not intercepted - CelerSearch disabled or no search area', [
				'search_term' => $search_term,
			] );

			return $query_args;
		}

		// Get the appropriate index
		$index_config = $this->get_index_for_orders();

		if ( ! $index_config ) {
			Logger::channel( Logger::CHANNEL_QUERIES )->debug( 'No index found for orders query', [
				'search_term' => $search_term,
			] );

			return $query_args;
		}

		try {
			$start_time = microtime( true );

			// Create index instance from config ID
			$index = IndexFactory::create( $index_config->getId() );

			// Perform search
			$results = $this->perform_search( $search_term, $index, $query_args );

			$duration_ms = round( ( microtime( true ) - $start_time ) * 1000, 2 );

			// Extract order IDs from results
			$order_ids = [];
			foreach ( $results['hits'] ?? [] as $hit ) {
				// OrdersIndex uses 'id' or 'order_id' field for the order ID
				$order_id = $hit['order_id'] ?? $hit['id'] ?? null;
				if ( $order_id ) {
					$order_ids[] = (int) $order_id;
				}
			}

			Logger::channel( Logger::CHANNEL_QUERIES )->info( 'Order search executed', [
				'search_term'   => $search_term,
				'index_slug'    => $index->get_slug(),
				'results_count' => count( $order_ids ),
				'total_hits'    => $results['estimatedTotalHits'] ?? 0,
				'duration_ms'   => $duration_ms,
			] );

			// Modify query args to return only our results
			return $this->inject_results( $query_args, $order_ids, $results );

		} catch ( \Exception $e ) {
			Logger::channel( Logger::CHANNEL_ERRORS )->error( 'Order query integration failed', [
				'search_term' => $search_term,
				'exception'   => $e->getMessage(),
				'index_id'    => $index_config->getId(),
			] );

			// On error, fall back to WooCommerce native search
			return $query_args;
		}
	}

	/**
	 * Check if query should be intercepted
	 *
	 * @return bool
	 */
	private function should_intercept(): bool {
		$settings = $this->get_settings();

		// Check if CelerSearch is enabled globally
		if ( empty( $settings['enable_search'] ) ) {
			return false;
		}

		// Check if there's a matching search area for orders
		$area = $this->get_search_area_for_orders();
		if ( ! $area ) {
			return false;
		}

		return apply_filters( 'celersearch_should_intercept_order_query', true );
	}

	/**
	 * Get search area for WooCommerce orders
	 *
	 * @return array|null
	 */
	private function get_search_area_for_orders(): ?array {
		$settings = $this->get_settings();
		$areas    = $settings['search_areas'] ?? [];

		// Find matching enabled search area for orders
		foreach ( $areas as $area ) {
			if ( isset( $area['type'] ) && $area['type'] === 'woocommerce_orders_search' && ! empty( $area['enabled'] ) ) {
				return $area;
			}
		}

		return null;
	}

	/**
	 * Get index configuration for orders
	 *
	 * @return IndexConfig|null
	 */
	private function get_index_for_orders(): ?IndexConfig {
		$repo = new IndexRepository();

		// Priority 1: Search area configuration
		$area = $this->get_search_area_for_orders();
		if ( $area && ! empty( $area['index_id'] ) ) {
			try {
				$index = $repo->find( $area['index_id'] );
				if ( $index ) {
					Logger::channel( Logger::CHANNEL_QUERIES )->debug( 'Orders index selected by search area', [
						'index_id' => $area['index_id'],
					] );

					return $index;
				}
			} catch ( \Exception $e ) {
				Logger::channel( Logger::CHANNEL_QUERIES )->warning( 'Orders search area index lookup failed', [
					'index_id'  => $area['index_id'],
					'exception' => $e->getMessage(),
				] );
			}
		}

		// Priority 2: Filter hook (allows custom logic)
		$index = apply_filters( 'celersearch_select_orders_index', null );
		if ( $index instanceof IndexConfig ) {
			return $index;
		}

		// Priority 3: Default index (if configured)
		$settings = $this->get_settings();
		if ( isset( $settings['default_index_id'] ) && $settings['default_index_id'] > 0 ) {
			try {
				$found_index = $repo->find( $settings['default_index_id'] );
				if ( $found_index ) {
					// Verify this is an orders index
					if ( $found_index->getType() === 'orders' ) {
						Logger::channel( Logger::CHANNEL_QUERIES )->debug( 'Orders index selected by default setting', [
							'index_id' => $settings['default_index_id'],
						] );

						return $found_index;
					}
				}
			} catch ( \Exception $e ) {
				Logger::channel( Logger::CHANNEL_QUERIES )->warning( 'Default orders index not found', [
					'index_id'  => $settings['default_index_id'],
					'exception' => $e->getMessage(),
				] );
			}
		}

		return null;
	}

	/**
	 * Perform search using index
	 *
	 * @param string    $search_term The search term.
	 * @param BaseIndex $index       The index to search.
	 * @param array     $query_args  The original query args for context.
	 *
	 * @return array
	 */
	private function perform_search( string $search_term, BaseIndex $index, array $query_args ): array {
		$per_page = $query_args['limit'] ?? 20;
		$page     = $query_args['paged'] ?? 1;

		// Build search parameters
		$params = [
			'q'      => $search_term,
			'limit'  => $per_page,
			'offset' => ( $page - 1 ) * $per_page,
		];

		// Apply status filter if set in query args
		if ( ! empty( $query_args['status'] ) ) {
			$status = $query_args['status'];
			// Normalize status format (remove 'wc-' prefix if present)
			if ( is_array( $status ) ) {
				$statuses = array_map( function( $s ) {
					return str_replace( 'wc-', '', $s );
				}, $status );
			} else {
				$statuses = [ str_replace( 'wc-', '', $status ) ];
			}

			$params['filters'] = [
				[
					'field'    => 'status',
					'operator' => count( $statuses ) === 1 ? '=' : 'IN',
					'value'    => count( $statuses ) === 1 ? $statuses[0] : $statuses,
				],
			];
		}

		// Allow modification of search parameters
		$params = apply_filters( 'celersearch_order_search_params', $params, $search_term, $query_args, $index );

		// Perform search
		try {
			$response = $index->search( $search_term, $params );

			// Convert SearchResponse object to array format
			if ( method_exists( $response, 'get_hits' ) ) {
				return [
					'hits'               => $response->get_hits(),
					'estimatedTotalHits' => $response->get_total_hits(),
					'totalPages'         => $response->get_total_pages(),
					'hitsPerPage'        => $response->get_hits_per_page(),
					'page'               => $response->get_current_page(),
				];
			}
		} catch ( \Exception $e ) {
			Logger::channel( Logger::CHANNEL_ERRORS )->error( 'Order search error', [
				'search_term' => $search_term,
				'index_slug'  => $index->get_slug(),
				'exception'   => $e->getMessage(),
			] );
		}

		return [];
	}

	/**
	 * Inject search results into query args
	 *
	 * @param array $query_args The original query args.
	 * @param array $order_ids  The matching order IDs.
	 * @param array $results    The full search results.
	 *
	 * @return array
	 */
	private function inject_results( array $query_args, array $order_ids, array $results ): array {
		// If no results found, return a query that will find nothing
		if ( empty( $order_ids ) ) {
			$query_args['include'] = [ 0 ];

			return $query_args;
		}

		// Set the query to only include our found orders
		$query_args['include'] = $order_ids;

		// Set orderby to 'include' to preserve relevance order from search results
		$query_args['orderby'] = 'include';

		// Remove the search term to prevent WooCommerce from doing its own search
		unset( $query_args['s'] );

		// Store total hits for pagination support
		add_filter( 'woocommerce_order_list_table_search_order_ids', function( $ids ) use ( $order_ids ) {
			return $order_ids;
		} );

		return $query_args;
	}

	/**
	 * Get CelerSearch settings
	 *
	 * @return array
	 */
	private function get_settings(): array {
		$defaults = [
			'enable_search'      => false,
			'default_index_id'   => 0,
			'search_areas'       => [],
			'fallback_to_native' => true,
		];

		$settings = get_option( 'celersearch_settings', [] );

		return wp_parse_args( $settings, $defaults );
	}
}
