<?php
/**
 * /products/search route (see docs/SPEC.md §8).
 *
 * @package EmberPrice
 */

namespace EmberPrice\Rest;

defined( 'ABSPATH' ) || exit;

/**
 * Registers the product search route: a thin proxy over WooCommerce's own
 * product search, used by the review step's inline "assign" picker
 * (T-207) when a supplier row needs to be pointed at a product manually.
 */
final class ProductsController extends Controller {

	/**
	 * Max results returned per search -- this backs a live "type to
	 * search" picker, not a paginated listing.
	 */
	private const MAX_RESULTS = 20;

	/**
	 * REST base for this resource.
	 *
	 * @var string
	 */
	protected $rest_base = 'products';

	/**
	 * Registers the /products/search route.
	 */
	public function register_routes(): void {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/search',
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'search' ),
				'permission_callback' => array( $this, 'permissions_check' ),
				'args'                => array(
					'q' => array(
						'type'     => 'string',
						'required' => true,
					),
				),
			)
		);
	}

	/**
	 * `GET /products/search?q=`: matches by name or SKU (WooCommerce's own
	 * product search already covers both). Variable products are expanded
	 * into one result per variation, since a row must be matched to the
	 * specific priced item, not the parent.
	 *
	 * @param \WP_REST_Request $request The current request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function search( \WP_REST_Request $request ) {
		$query = trim( (string) $request->get_param( 'q' ) );

		if ( '' === $query ) {
			return new \WP_REST_Response( array( 'results' => array() ), 200 );
		}

		$products = wc_get_products(
			array(
				'status' => 'publish',
				'limit'  => self::MAX_RESULTS,
				's'      => $query,
				'return' => 'objects',
			)
		);

		$results = array();

		foreach ( $products as $product ) {
			if ( $product instanceof \WC_Product_Variable ) {
				array_push( $results, ...$this->variation_results( $product ) );
				continue;
			}

			$results[] = $this->product_result( $product );

			if ( count( $results ) >= self::MAX_RESULTS ) {
				break;
			}
		}

		return new \WP_REST_Response( array( 'results' => array_slice( $results, 0, self::MAX_RESULTS ) ), 200 );
	}

	/**
	 * Shapes a single simple-product search result.
	 *
	 * @param \WC_Product $product A simple (or otherwise non-variable) product.
	 */
	private function product_result( \WC_Product $product ): array {
		return array(
			'product_id'   => $product->get_id(),
			'variation_id' => null,
			'sku'          => $product->get_sku(),
			'name'         => $product->get_name(),
			'price'        => $product->get_price(),
		);
	}

	/**
	 * Shapes one search result per variation of a variable product. `name`
	 * is built as "Parent → attributes" (SPEC/T-307's "Producto →
	 * Variación"), the same format `Rest\Controller::with_matched_product()`
	 * uses for a matched row, rather than `WC_Product_Variation::get_name()`.
	 *
	 * @param \WC_Product_Variable $product Parent variable product.
	 *
	 * @return array<int, array>
	 */
	private function variation_results( \WC_Product_Variable $product ): array {
		$results = array();

		foreach ( $product->get_children() as $variation_id ) {
			$variation = wc_get_product( $variation_id );

			if ( ! $variation instanceof \WC_Product_Variation ) {
				continue;
			}

			$results[] = array(
				'product_id'   => $product->get_id(),
				'variation_id' => $variation->get_id(),
				'sku'          => $variation->get_sku(),
				'name'         => $product->get_name() . ' → ' . $variation->get_attribute_summary(),
				'price'        => $variation->get_price(),
			);
		}

		return $results;
	}
}
