<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Selects a purchasable variation image from verified product constraints.
 */
class Niwa_AI_Product_Variation_Preview {
	/**
	 * @param array $variations Search evidence variations.
	 * @param array $queries Unstructured search terms used only without constraints.
	 * @param array $constraints Structured product requirements.
	 * @return array<string,mixed>
	 */
	public static function resolve( $variations, $queries = array(), $constraints = array() ) {
		$constraints = class_exists( 'Niwa_AI_Frontend_Product_Query_Plan' )
			? Niwa_AI_Frontend_Product_Query_Plan::normalize_constraints( $constraints )
			: array();
		if ( ! empty( $constraints ) ) {
			return self::resolve_constraints( $variations, $constraints );
		}

		$query_roots = self::roots( implode( ' ', array_map( 'strval', (array) $queries ) ) );
		if ( empty( $query_roots ) ) {
			return array();
		}

		$best       = array();
		$best_score = 0;
		foreach ( (array) $variations as $variation ) {
			if ( ! is_array( $variation ) || empty( $variation['image'] ) || empty( $variation['purchasable'] ) ) {
				continue;
			}

			$values = array_values( array_filter( array_map( 'strval', (array) ( $variation['attributes'] ?? array() ) ) ) );
			$matches = array_values( array_intersect( $query_roots, self::roots( implode( ' ', $values ) ) ) );
			$score   = count( array_unique( $matches ) );
			if ( $score <= $best_score ) {
				continue;
			}

			$best_score = $score;
			$best       = self::preview( $variation, $matches );
		}

		return $best;
	}

	/**
	 * Require all variation-side constraints on one real child. Discovery-only
	 * related/parent terms never participate in the displayed image.
	 */
	private static function resolve_constraints( $variations, $constraints ) {
		$eligible = array_values(
			array_filter(
				(array) $variations,
				static function ( $variation ) {
					return is_array( $variation ) && ! empty( $variation['image'] ) && ! empty( $variation['purchasable'] );
				}
			)
		);
		if ( empty( $eligible ) ) {
			return array();
		}

		$required = array();
		$optional = array();
		$excluded = array();
		foreach ( (array) $constraints as $constraint ) {
			$matching = array();
			foreach ( $eligible as $index => $variation ) {
				$values = array_values( array_filter( array_map( 'strval', (array) ( $variation['attributes'] ?? array() ) ) ) );
				if ( Niwa_AI_Frontend_Product_Query_Plan::constraint_matches_values( $values, $constraint ) ) {
					$matching[ $index ] = true;
				}
			}
			if ( empty( $matching ) ) {
				continue;
			}
			if ( 'exclude' === (string) ( $constraint['polarity'] ?? 'include' ) ) {
				$excluded[] = $matching;
			} elseif ( ! empty( $constraint['required'] ) ) {
				$required[] = $matching;
			} else {
				$optional[] = $matching;
			}
		}
		if ( empty( $required ) && empty( $optional ) && empty( $excluded ) ) {
			return array();
		}

		$best_index = -1;
		$best_score = -1;
		foreach ( $eligible as $index => $variation ) {
			foreach ( $excluded as $matching ) {
				if ( isset( $matching[ $index ] ) ) {
					continue 2;
				}
			}
			foreach ( $required as $matching ) {
				if ( ! isset( $matching[ $index ] ) ) {
					continue 2;
				}
			}
			$score = 0;
			foreach ( $optional as $matching ) {
				$score += isset( $matching[ $index ] ) ? 1 : 0;
			}
			if ( $score > $best_score ) {
				$best_index = $index;
				$best_score = $score;
			}
		}

		return $best_index >= 0
			? self::preview( $eligible[ $best_index ], array_values( array_filter( array_map( 'strval', (array) ( $eligible[ $best_index ]['attributes'] ?? array() ) ) ) ) )
			: array();
	}

	private static function preview( $variation, $matches ) {
		return array(
			'variation_id'   => intval( $variation['variation_id'] ?? 0 ),
			'image'          => (string) $variation['image'],
			'image_width'    => intval( $variation['image_width'] ?? 0 ),
			'image_height'   => intval( $variation['image_height'] ?? 0 ),
			'attributes'     => (array) ( $variation['attributes'] ?? array() ),
			'selection'      => (array) ( $variation['selection'] ?? array() ),
			'matched_values' => array_values( array_unique( array_filter( array_map( 'strval', (array) $matches ) ) ) ),
		);
	}

	private static function roots( $text ) {
		$text = html_entity_decode( mb_strtolower( (string) $text ), ENT_QUOTES | ENT_HTML5, 'UTF-8' );
		if ( function_exists( 'remove_accents' ) ) {
			$text = remove_accents( $text );
		}
		$text   = trim( preg_replace( '/[^\p{L}\p{N}]+/u', ' ', $text ) );
		$tokens = '' === $text ? array() : preg_split( '/\s+/u', $text );
		$roots  = array();
		foreach ( (array) $tokens as $token ) {
			$length = mb_strlen( $token );
			if ( $length < 2 ) {
				continue;
			}
			$roots[] = $token;
			if ( $length >= 4 ) {
				$roots[] = mb_substr( $token, 0, $length - 1 );
			}
			if ( $length >= 7 ) {
				$roots[] = mb_substr( $token, 0, $length - 2 );
			}
		}

		return array_values( array_unique( array_filter( $roots ) ) );
	}
}
