<?php
/**
 * List products use case.
 *
 * Phase 6 Application-layer UseCase that drives the §8.2
 * `GET /affilun/v1/products` endpoint. The class is a thin wrapper
 * around
 * {@see \Affilun\Infrastructure\Persistence\ProductRepository::findPaginated()}:
 * it accepts the normalized {@see ProductListQuery} the REST
 * controller built from the request query string and returns the
 * {@see ProductListResult} envelope the controller will render
 * onto the §4.2 pagination response shape.
 *
 * Why a thin wrapper exists despite delegating directly:
 *
 *   - Consistency with the other three product UseCases the
 *     Phase 6b controller composes
 *     ({@see SearchProductsUseCase},
 *     {@see GetProductUseCase}, {@see SaveProductUseCase}). The
 *     controller's dependency surface is uniform — every endpoint
 *     resolves a single UseCase and calls `execute()` on it — so a
 *     direct repository injection at the controller layer would
 *     introduce an asymmetry the §6 layering rules deliberately
 *     forbid (`coding-standards.md` §1.3 B keeps Application
 *     concerns out of the Presentation layer).
 *
 *   - Future extension point. The §8.2 contract's surface is
 *     stable today, but several Phase-6+ extensions (per-user
 *     authorization filtering, tenant scoping, response-shape
 *     caching keyed by the normalized query, audit logging of
 *     "who listed which products when?") naturally land on the
 *     Application layer rather than the Infrastructure repository
 *     port. Routing every list request through this UseCase
 *     today means those extensions can ship without touching
 *     the controller wiring.
 *
 *   - Test ergonomics. The Phase 6b controller tests stub a
 *     UseCase rather than a repository so the controller's
 *     edge cases (parameter normalization, error mapping,
 *     response shape) can be exercised without standing up an
 *     in-memory repository fixture. Keeping the UseCase as a
 *     dedicated type — even when its body is one line —
 *     preserves that test seam.
 *
 * The UseCase performs no validation of the supplied query
 * because the {@see ProductListQuery} value object validates its
 * own invariants at construction time; the §5.3 closed-set tokens
 * for `sort`, `order`, `sync_state`, and `lifecycle_state` are
 * already enforced before the query reaches this method.
 *
 * @package Affilun\Application\Product\UseCase
 */

declare(strict_types=1);

namespace Affilun\Application\Product\UseCase;

use Affilun\Infrastructure\Persistence\ProductListQuery;
use Affilun\Infrastructure\Persistence\ProductListResult;
use Affilun\Infrastructure\Persistence\ProductRepository;

/**
 * Phase 6 UseCase that drives the §8.2 `GET /affilun/v1/products`
 * endpoint.
 *
 * See the file-level docblock for the rationale that justifies
 * the thin wrapper despite the direct repository delegation, and
 * the future extension points (authorization filtering, caching,
 * audit logging) the wrapper preserves space for.
 */
final class ListProductsUseCase {

	/**
	 * Persistence port that backs the §8.2 listing.
	 */
	private ProductRepository $repository;

	/**
	 * Construct the UseCase with an explicit repository binding.
	 *
	 * @param ProductRepository $repository Persistence port.
	 */
	public function __construct( ProductRepository $repository ) {
		$this->repository = $repository;
	}

	/**
	 * Execute the §8.2 list dispatch.
	 *
	 * Delegates verbatim to
	 * {@see ProductRepository::findPaginated()}; the supplied
	 * query's invariants are already enforced at its construction
	 * site so no further validation runs here.
	 *
	 * @param ProductListQuery $query Normalized list query.
	 *
	 * @return ProductListResult Page slice plus pagination totals.
	 */
	public function execute( ProductListQuery $query ): ProductListResult {
		return $this->repository->findPaginated( $query );
	}
}
