<?php
/**
 * Bounded reconciliation of previous file findings.
 *
 * @package LocalGuardScanner
 */

namespace LocalGuardScanner\Application\Findings;

use LocalGuardScanner\Domain\Scan\InspectionScope;
use LocalGuardScanner\Infrastructure\Database\FindingRepository;

/**
 * Advances one stable-ID batch and returns cursor-safe aggregate state.
 */
final class MissingFileReconciler {
	/** @var MissingFileVerifier */
	private $verifier;

	/** @param MissingFileVerifier|null $verifier Individual verifier. */
	public function __construct( ?MissingFileVerifier $verifier = null ) {
		$this->verifier = null === $verifier ? new MissingFileVerifier() : $verifier;
	}

	/**
	 * @param FindingRepository  $repository Finding repository.
	 * @param int                $scan_id Current scan ID.
	 * @param string             $root WordPress root.
	 * @param InspectionScope    $scope Scan exclusions.
	 * @param array<string,mixed> $state Previous cursor state.
	 * @param int                $limit Batch limit.
	 * @param string             $now UTC MySQL time.
	 * @return array{last_id:int,checked:int,resolved:int,done:bool}
	 */
	public function next_batch( FindingRepository $repository, $scan_id, $root, InspectionScope $scope, array $state, $limit, $now ) {
		$state = $this->normalize( $state );
		$limit = max( 25, min( 100, (int) $limit ) );
		if ( $state['done'] ) {
			return $state;
		}

		$candidates  = $repository->stale_file_candidates( (int) $scan_id, $state['last_id'], $limit );
		$missing_ids = array();
		foreach ( $candidates as $candidate ) {
			$state['last_id'] = max( $state['last_id'], (int) $candidate['id'] );
			++$state['checked'];
			if ( MissingFileVerifier::MISSING === $this->verifier->verify( $root, $candidate['locator'], $scope ) ) {
				$missing_ids[] = (int) $candidate['id'];
			}
		}
		if ( ! empty( $missing_ids ) ) {
			$state['resolved'] += $repository->resolve_missing_file_ids( (int) $scan_id, $missing_ids, $now );
		}
		$state['done'] = count( $candidates ) < $limit;
		return $state;
	}

	/**
	 * @param array<string,mixed> $state Raw state.
	 * @return array{last_id:int,checked:int,resolved:int,done:bool}
	 */
	private function normalize( array $state ) {
		return array(
			'last_id'  => isset( $state['last_id'] ) ? max( 0, (int) $state['last_id'] ) : 0,
			'checked'  => isset( $state['checked'] ) ? max( 0, (int) $state['checked'] ) : 0,
			'resolved' => isset( $state['resolved'] ) ? max( 0, (int) $state['resolved'] ) : 0,
			'done'     => ! empty( $state['done'] ),
		);
	}
}
