<?php
/**
 * Candidate product service.
 *
 * @package Ugcify\AI
 */

namespace Ugcify\AI;

use Ugcify\Database\ReviewRepository;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class CandidateProductService
 */
class CandidateProductService {
	/**
	 * Review repository.
	 *
	 * @var ReviewRepository
	 */
	private $reviews;

	/**
	 * Descriptor service.
	 *
	 * @var ProductDescriptorService
	 */
	private $descriptors;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->reviews     = new ReviewRepository();
		$this->descriptors = new ProductDescriptorService();
	}

	/**
	 * Get candidate products for a review image.
	 *
	 * @param int $review_id Review ID.
	 * @param int $limit Limit.
	 * @return array<int,array<string,mixed>>
	 */
	public function get_candidates( int $review_id, int $limit = 8 ): array {
		if ( ! function_exists( 'wc_get_product' ) || ! function_exists( 'wc_get_products' ) ) {
			return array();
		}

		$limit     = max( 1, min( 20, $limit ) );
		$review    = $this->reviews->find( $review_id );
		$product_ids = array();

		if ( $review && ! empty( $review->product_id ) ) {
			$product_ids[] = (int) $review->product_id;
			$linked        = wc_get_product( (int) $review->product_id );
			if ( $linked ) {
				foreach ( $this->get_related_products( $linked, $limit ) as $related_id ) {
					$product_ids[] = $related_id;
				}
			}
		}

		if ( count( array_unique( $product_ids ) ) < $limit ) {
			$recent = wc_get_products(
				array(
					'status'  => array( 'publish' ),
					'limit'   => $limit,
					'orderby' => 'date',
					'order'   => 'DESC',
					'return'  => 'ids',
				)
			);

			foreach ( (array) $recent as $product_id ) {
				$product_ids[] = (int) $product_id;
			}
		}

		$product_ids = array_values( array_unique( array_filter( array_map( 'absint', $product_ids ) ) ) );
		$candidates  = array();

		foreach ( $product_ids as $product_id ) {
			if ( count( $candidates ) >= $limit ) {
				break;
			}

			$product = wc_get_product( $product_id );
			if ( ! $product || 'publish' !== get_post_status( $product_id ) ) {
				continue;
			}

			$candidates[] = $this->build_candidate_payload( $product );
		}

		return $candidates;
	}

	/**
	 * Get related product IDs.
	 *
	 * @param \WC_Product $product Product.
	 * @param int         $limit Limit.
	 * @return array<int,int>
	 */
	private function get_related_products( $product, int $limit ): array {
		$product_id = (int) $product->get_id();
		$ids        = array();
		$categories = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'slugs' ) );

		if ( ! is_wp_error( $categories ) && ! empty( $categories ) ) {
			$products = wc_get_products(
				array(
					'status'   => array( 'publish' ),
					'limit'    => $limit,
					'category' => array_values( array_map( 'sanitize_title', (array) $categories ) ),
					'return'   => 'ids',
				)
			);

			foreach ( (array) $products as $id ) {
				$ids[] = (int) $id;
			}
		}

		foreach ( wc_get_related_products( $product_id, $limit ) as $id ) {
			$ids[] = (int) $id;
		}

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

	/**
	 * Build candidate payload.
	 *
	 * @param \WC_Product $product Product.
	 * @return array<string,mixed>
	 */
	private function build_candidate_payload( $product ): array {
		$product_id = (int) $product->get_id();

		return array(
			'id'          => $product_id,
			'title'       => $product->get_name(),
			'sku'         => $product->get_sku(),
			'categories'  => $this->get_term_names( $product_id, 'product_cat' ),
			'tags'        => $this->get_term_names( $product_id, 'product_tag' ),
			'attributes'  => $this->get_attribute_values( $product ),
			'descriptor'  => $this->descriptors->get_descriptor( $product ),
			'image_url'   => wp_get_attachment_image_url( (int) $product->get_image_id(), 'medium' ) ?: '',
			'is_in_stock' => $product->is_in_stock(),
			'is_purchasable' => $product->is_purchasable(),
		);
	}

	/**
	 * Get term names.
	 *
	 * @param int    $product_id Product ID.
	 * @param string $taxonomy Taxonomy.
	 * @return array<int,string>
	 */
	private function get_term_names( int $product_id, string $taxonomy ): array {
		$terms = get_the_terms( $product_id, $taxonomy );
		if ( empty( $terms ) || is_wp_error( $terms ) ) {
			return array();
		}

		return array_values(
			array_filter(
				array_map(
					static function ( $term ) {
						return isset( $term->name ) ? (string) $term->name : '';
					},
					$terms
				)
			)
		);
	}

	/**
	 * Get attribute values.
	 *
	 * @param \WC_Product $product Product.
	 * @return array<int,string>
	 */
	private function get_attribute_values( $product ): array {
		$values = array();

		foreach ( $product->get_attributes() as $attribute ) {
			if ( is_object( $attribute ) && method_exists( $attribute, 'get_options' ) ) {
				$values = array_merge( $values, array_map( 'strval', (array) $attribute->get_options() ) );
			}
		}

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