<?php

namespace CelerSearch\Indices;

defined( 'ABSPATH' ) || exit;

use CelerSearch\Builders\IndexSettingsBuilder;
use CelerSearch\DataTransfer\IndexCandidate;
use CelerSearch\DataTransfer\IndexSettings;
use CelerSearch\DataTransfer\IndexableOrder;
use CelerSearch\Interfaces\IIndexableObject;

class OrdersIndex extends BaseIndex {

	/**
	 * The allowed order statuses
	 * @var array
	 */
	protected array $order_statuses = [];

	/**
	 * Performs additional initialization (if needed)
	 * @return void
	 */
	protected function init(): void {
		$config = $this->details->getConfig();

		// Get order statuses from config, or use all statuses if not set
		if ( isset( $config->order_statuses ) ) {
			$this->order_statuses = $config->order_statuses;
			// If order_statuses is an object (due to array to object conversion), convert back to array
			if ( is_object( $this->order_statuses ) ) {
				$this->order_statuses = (array) $this->order_statuses;
			}
		} else {
			// Default to all order statuses
			$this->order_statuses = array_keys( wc_get_order_statuses() );
		}
	}

	/**
	 * Check whether an item is supported.
	 *
	 * @param IndexCandidate $item
	 *
	 * @return bool
	 */
	public function is_supported( IndexCandidate $item ): bool {
		return $item && is_a( $item->item, \WC_Order::class );
	}

	/**
	 * Check whether an item can be indexed.
	 *
	 * @param IndexCandidate $item
	 *
	 * @return bool
	 */
	public function should_index( IndexCandidate $item ): bool {
		if ( ! $this->is_supported( $item ) ) {
			return false;
		}

		// Check if WooCommerce is active
		if ( ! function_exists( 'wc_get_order' ) ) {
			return false;
		}

		$order  = $item->item;
		$status = 'wc-' . $order->get_status();

		// If no statuses configured, index all orders
		if ( empty( $this->order_statuses ) ) {
			return true;
		}

		return in_array( $status, $this->order_statuses, true );
	}

	/**
	 * Returns items from the index
	 *
	 * @param int $page
	 * @param int $batch_size
	 *
	 * @return array<IndexCandidate>
	 */
	public function get_candidates( int $page, int $batch_size ): array {
		$args = [
			'limit'   => $batch_size,
			'paged'   => $page,
			'orderby' => 'ID',
			'order'   => 'ASC',
			'type'    => 'shop_order',
		];

		// Add status filter if configured
		if ( ! empty( $this->order_statuses ) ) {
			$args['status'] = $this->order_statuses;
		}

		$orders = wc_get_orders( $args );

		return array_map( function( \WC_Order $order ) {
			return new IndexCandidate( $order );
		}, $orders );
	}

	/**
	 * Returns specific items by IDs
	 *
	 * @param array $ids
	 *
	 * @return array<IndexCandidate>
	 */
	public function get_candidates_by_ids( array $ids ): array {
		$args = [
			'include' => $ids,
			'limit'   => count( $ids ),
			'type'    => 'shop_order',
		];

		$orders = wc_get_orders( $args );

		return array_map( function( \WC_Order $order ) {
			return new IndexCandidate( $order );
		}, $orders );
	}

	/**
	 * Returns items count from the index
	 *
	 * @return int
	 */
	public function get_candidate_count(): int {
		$args = [
			'type'   => 'shop_order',
			'return' => 'ids',
			'limit'  => -1,
		];

		// Add status filter if configured
		if ( ! empty( $this->order_statuses ) ) {
			$args['status'] = $this->order_statuses;
		}

		$order_ids = wc_get_orders( $args );

		return count( $order_ids );
	}

	/**
	 * Returns the item records
	 *
	 * @param IndexCandidate $item
	 *
	 * @return IIndexableObject[]
	 */
	public function get_candidate_objects( IndexCandidate $item ): array {
		return $this->create_records( $item->item );
	}

	/**
	 * Creates order record
	 *
	 * @param \WC_Order $order
	 *
	 * @return IndexableOrder[]
	 */
	private function create_records( \WC_Order $order ): array {
		if ( ! defined( 'WC_VERSION' ) ) {
			return [];
		}

		$attributes = $this->get_attributes( $order );
		$object_id  = 'order-' . $order->get_id();

		$records   = [];
		$records[] = new IndexableOrder( $object_id, $attributes );

		$records = (array) apply_filters( 'celersearch_searchable_order_records', $records, $order );

		return $records;
	}

	/**
	 * Get order attributes for indexing.
	 *
	 * @param \WC_Order $order The order object.
	 *
	 * @return array
	 */
	private function get_attributes( \WC_Order $order ): array {
		$date_created           = $order->get_date_created();
		$date_created_timestamp = null !== $date_created ? $date_created->getTimestamp() : 0;
		$date_created_i18n      = null !== $date_created ? $date_created->date_i18n( get_option( 'date_format' ) ) : '';

		$attributes = [
			'object_id'              => 'order-' . $order->get_id(),
			'id'                     => (int) $order->get_id(),
			'order_id'               => (int) $order->get_id(),
			'type'                   => $order->get_type(),
			'number'                 => (string) $order->get_order_number(),
			'status'                 => $order->get_status(),
			'status_name'            => wc_get_order_status_name( $order->get_status() ),
			'date_timestamp'         => $date_created_timestamp,
			'date_formatted'         => $date_created_i18n,
			'order_total'            => (float) $order->get_total(),
			'formatted_order_total'  => $order->get_formatted_order_total(),
			'items_count'            => (int) $order->get_item_count(),
			'payment_method_title'   => $order->get_payment_method_title(),
			'shipping_method_title'  => $order->get_shipping_method(),
		];

		// Add user/customer info
		$user = $order->get_user();
		if ( $user ) {
			$attributes['customer'] = [
				'id'           => (int) $user->ID,
				'display_name' => trim( $user->first_name . ' ' . $user->last_name ),
				'email'        => $user->user_email,
			];
		} else {
			// Guest customer - use billing info
			$attributes['customer'] = [
				'id'           => 0,
				'display_name' => $order->get_formatted_billing_full_name(),
				'email'        => $order->get_billing_email(),
			];
		}

		// Billing address with country name resolution
		$billing_country = $order->get_billing_country();
		$billing_country = isset( WC()->countries->countries[ $billing_country ] ) ? WC()->countries->countries[ $billing_country ] : $billing_country;

		$attributes['billing'] = [
			'display_name' => $order->get_formatted_billing_full_name(),
			'email'        => $order->get_billing_email(),
			'phone'        => $order->get_billing_phone(),
			'company'      => $order->get_billing_company(),
			'address_1'    => $order->get_billing_address_1(),
			'address_2'    => $order->get_billing_address_2(),
			'city'         => $order->get_billing_city(),
			'state'        => $order->get_billing_state(),
			'postcode'     => $order->get_billing_postcode(),
			'country'      => $billing_country,
		];

		// Shipping address with country name resolution
		$shipping_country = $order->get_shipping_country();
		$shipping_country = isset( WC()->countries->countries[ $shipping_country ] ) ? WC()->countries->countries[ $shipping_country ] : $shipping_country;

		$attributes['shipping'] = [
			'display_name' => $order->get_formatted_shipping_full_name(),
			'company'      => $order->get_shipping_company(),
			'address_1'    => $order->get_shipping_address_1(),
			'address_2'    => $order->get_shipping_address_2(),
			'city'         => $order->get_shipping_city(),
			'state'        => $order->get_shipping_state(),
			'postcode'     => $order->get_shipping_postcode(),
			'country'      => $shipping_country,
		];

		// Add line items
		$attributes['items'] = [];
		foreach ( $order->get_items() as $item_id => $order_item ) {
			$product = $order_item->get_product();

			$attributes['items'][] = [
				'id'   => (int) $item_id,
				// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce core filter
				'name' => apply_filters( 'woocommerce_order_item_name', esc_html( $order_item->get_name() ), $order_item, false ),
				'qty'  => (int) $order_item->get_quantity(),
				'sku'  => $product instanceof \WC_Product ? $product->get_sku() : '',
			];
		}

		// Admin URL for linking
		$attributes['edit_url']  = $order->get_edit_order_url();
		$attributes['permalink'] = $order->get_edit_order_url();

		return (array) apply_filters( 'celersearch_searchable_order_attributes', $attributes, $order );
	}

	/**
	 * Create order index settings
	 *
	 * @return IndexSettings
	 */
	public function get_settings(): IndexSettings {

		$settings = ( new IndexSettingsBuilder )
			->from_stored( $this->get_stored_settings() )
			->primary_key( 'object_id' )
			->distinct_attribute( 'order_id' )
			->searchable_attributes( [
				'id',
				'number',
				'customer.display_name',
				'customer.email',
				'billing.display_name',
				'shipping.display_name',
				'billing.email',
				'billing.phone',
				'billing.company',
				'shipping.company',
				'billing.address_1',
				'shipping.address_1',
				'billing.address_2',
				'shipping.address_2',
				'billing.city',
				'shipping.city',
				'billing.state',
				'shipping.state',
				'billing.postcode',
				'shipping.postcode',
				'billing.country',
				'shipping.country',
				'items.sku',
				'items.name',
				'status_name',
				'order_total',
			] )
			->filterable_attributes( [
				'status',
				'status_name',
				'date_timestamp',
				'billing.country',
				'shipping.country',
				'payment_method_title',
				'order_total',
				'customer.display_name',
				'items.sku',
			] )
			->sortable_attributes( [
				'date_timestamp:desc',
				'order_total:desc',
				'id:desc',
			] )
			->snippet_attributes( [] );

		$settings = apply_filters( 'celersearch_orders_index_settings', $settings, $this );

		return $settings->build();
	}

	/**
	 * Format an order hit for autocomplete display
	 *
	 * @param array $hit Raw hit from search index
	 *
	 * @return array|null Formatted hit or null to skip
	 */
	public function format_autocomplete_hit( array $hit ): ?array {
		$order_id = $hit['order_id'] ?? $hit['id'] ?? 0;

		if ( empty( $order_id ) ) {
			return null;
		}

		// Build customer info for excerpt
		$customer_name  = $hit['customer']['display_name'] ?? $hit['billing']['display_name'] ?? '';
		$customer_email = $hit['customer']['email'] ?? $hit['billing']['email'] ?? '';
		$excerpt_parts  = array_filter( [ $customer_name, $customer_email ] );
		$excerpt        = implode( ' - ', $excerpt_parts );

		// Add order total if available
		if ( ! empty( $hit['formatted_order_total'] ) ) {
			$excerpt .= ( $excerpt ? ' | ' : '' ) . html_entity_decode( wp_strip_all_tags( $hit['formatted_order_total'] ), ENT_QUOTES | ENT_HTML5, 'UTF-8' );
		}

		return [
			'id'              => $order_id,
			'post_type'       => 'shop_order',
			'post_type_label' => __( 'Order', 'celersearch' ),
			'title'           => sprintf(
				/* translators: %1$s: order number, %2$s: order status */
				__( 'Order #%1$s (%2$s)', 'celersearch' ),
				$hit['number'] ?? $order_id,
				$hit['status_name'] ?? $hit['status'] ?? ''
			),
			'excerpt'         => $excerpt,
			'url'             => $hit['edit_url'] ?? $hit['permalink'] ?? '',
			'edit_url'        => $hit['edit_url'] ?? '',
			'thumbnail'       => $this->get_initials_avatar( $customer_name ),
			'variations'      => [],
			'_formatted'      => $this->sanitize_formatted_fields( $hit['_formatted'] ?? [] ),
		];
	}

	/**
	 * Generate an SVG data URI avatar with initials from a customer name.
	 *
	 * @param string $name Customer name.
	 *
	 * @return string SVG data URI.
	 */
	private function get_initials_avatar( string $name ): string {
		$name = trim( $name );

		if ( '' === $name ) {
			$initials = '?';
		} else {
			$words    = preg_split( '/\s+/', $name );
			$first    = mb_strtoupper( mb_substr( $words[0], 0, 1 ) );
			$last     = count( $words ) > 1 ? mb_strtoupper( mb_substr( end( $words ), 0, 1 ) ) : '';
			$initials = $first . $last;
		}

		$colors = [ '#6366f1', '#8b5cf6', '#ec4899', '#ef4444', '#f59e0b', '#10b981', '#06b6d4', '#3b82f6' ];
		$color  = $colors[ abs( crc32( $name ) ) % count( $colors ) ];

		$svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">'
			. '<circle cx="24" cy="24" r="24" fill="' . $color . '"/>'
			. '<text x="24" y="24" text-anchor="middle" dy=".35em" fill="#fff" font-family="sans-serif" font-size="18" font-weight="600">'
			. htmlspecialchars( $initials, ENT_XML1, 'UTF-8' )
			. '</text></svg>';

		return 'data:image/svg+xml,' . rawurlencode( $svg );
	}

	/**
	 * Get status filter for orders autocomplete search
	 *
	 * Orders are already filtered by status during indexing,
	 * so no additional status filter is needed for autocomplete.
	 *
	 * @param string $context 'frontend' or 'admin'
	 *
	 * @return array|null
	 */
	public function get_autocomplete_status_filter( string $context ): ?array {
		// Orders don't use WordPress post statuses
		// Status filtering is handled during indexing based on configured order statuses
		return null;
	}
}
