<?php
/**
 * Saved-product link-health URL collector.
 *
 * @package Affilun\Application\LinkHealth
 */

declare(strict_types=1);

namespace Affilun\Application\LinkHealth;

use Affilun\Infrastructure\Persistence\ProductListQuery;
use Affilun\Infrastructure\Persistence\ProductRecord;
use Affilun\Infrastructure\Persistence\ProductRepository;
use DateTimeImmutable;

/**
 * Selects a bounded saved-product URL batch for scheduled checks.
 */
final class SavedProductLinkHealthUrlCollector {

	/**
	 * Default number of product rows scanned per cron tick.
	 */
	public const DEFAULT_SCAN_PAGE_SIZE = 50;

	/**
	 * @param ProductRepository            $products    Product repository.
	 * @param ProductLinkHealthStatusStore $statusStore Status store.
	 */
	public function __construct(
		private ProductRepository $products,
		private ProductLinkHealthStatusStore $statusStore
	) {}

	/**
	 * Collect a bounded URL batch from active saved products.
	 *
	 * @param int               $page            Cursor page, 1-based.
	 * @param int               $scanPageSize    Product rows to scan, capped at 50.
	 * @param int               $submitLimit     Maximum URLs to submit.
	 * @param DateTimeImmutable $now             Current instant.
	 * @param int               $freshTtlSeconds Skip products fresh within this TTL.
	 */
	public function collect(
		int $page,
		int $scanPageSize,
		int $submitLimit,
		DateTimeImmutable $now,
		int $freshTtlSeconds
	): ScheduledLinkHealthCandidateBatch {
		$page         = max( 1, $page );
		$scanPageSize = max( 1, min( self::DEFAULT_SCAN_PAGE_SIZE, $scanPageSize ) );
		$submitLimit  = max( 1, $submitLimit );

		$result = $this->products->findPaginated(
			new ProductListQuery(
				page: $page,
				perPage: $scanPageSize,
				lifecycleState: ProductListQuery::LIFECYCLE_STATE_ACTIVE
			)
		);

		$urls     = array();
		$contexts = array();
		foreach ( $result->items() as $record ) {
			$productId = $record->id();
			if ( $productId === null || $productId <= 0 ) {
				continue;
			}
			if ( $this->statusStore->isFresh( $productId, $now, $freshTtlSeconds ) ) {
				continue;
			}

			$url = self::urlForProductRecord( $record );
			if ( $url === '' || array_key_exists( $url, $contexts ) ) {
				continue;
			}

			$urls[]           = $url;
			$contexts[ $url ] = array(
				'product_id'       => $productId,
				'provider'         => $record->provider(),
				'provider_item_id' => $record->providerItemId(),
				'label'            => self::labelForProductRecord( $record ),
			);
			if ( count( $urls ) >= $submitLimit ) {
				break;
			}
		}

		$nextPage = $page + 1;
		if ( $result->totalPages() === 0 || $page >= $result->totalPages() ) {
			$nextPage = 1;
		}

		return new ScheduledLinkHealthCandidateBatch(
			$urls,
			$contexts,
			count( $result->items() ),
			$nextPage
		);
	}

	/**
	 * Pick the URL to check for one saved product.
	 *
	 * @param ProductRecord $record Product record.
	 */
	private static function urlForProductRecord( ProductRecord $record ): string {
		$affiliateUrl = $record->affiliateUrl();
		if ( is_string( $affiliateUrl ) && $affiliateUrl !== '' ) {
			return $affiliateUrl;
		}

		return $record->detailUrl();
	}

	/**
	 * Build a compact label for one saved product.
	 *
	 * @param ProductRecord $record Product record.
	 */
	private static function labelForProductRecord( ProductRecord $record ): string {
		$id = $record->id();
		return sprintf(
			'#%s %s / %s',
			$id === null ? '-' : (string) $id,
			$record->provider(),
			$record->title()
		);
	}
}
