<?php

/**
 * Shared auto-retry loop for Plugin Builder generation + review.
 *
 * @package LolaCore\PluginBuilder\Generation
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\PluginBuilder\Generation;

use LolaCore\PluginBuilder\Handler\PluginBuilderHandler;

if (! defined('ABSPATH')) {
	exit;
}

/**
 * Runs ledger-backed retries with fix-mode (blocking) or regen-mode (confidence).
 */
class GenerationRetryOrchestrator {

	public function __construct(
		private PluginGenerator $generator,
		private CodeReviewer $reviewer
	) {
	}

	/**
	 * @param array{php: string, js: string, css: string, slug: string, dependencies?: array<int, string>} $generated
	 * @param array<string, mixed> $review
	 * @param callable|null        $log Optional logger: function( string $message ): void
	 * @return array{0: array, 1: array, 2: bool, 3: bool, 4: RetryLedger|null} [generated, review, retried, stuck, ledger]
	 */
	public function run(
		string $description,
		array $generated,
		array $review,
		bool $includeSiteContext = true,
		string $prd = '',
		?callable $log = null,
		int $libraryVersion = 1,
		?RetryLedger $seedLedger = null,
		bool $fixModeOnly = false
	): array {
		if (! PluginBuilderHandler::reviewNeedsRetry($review)) {
			return array($generated, $review, false, false, null);
		}

		if ($seedLedger !== null) {
			$ledger = $seedLedger;
			$ledger->addFromReview($review, $ledger->getNextAttemptIndex());
		} else {
			$ledger = new RetryLedger();
			$ledger->addFromReview($review, 0);
		}

		$this->log($log, sprintf(
			'retry TRIGGER — %s | ledger entries=%d codes=[%s]',
			PluginBuilderHandler::formatRetryTriggerReason($review),
			$ledger->countEntries(),
			implode(',', $ledger->getLastFatalCodes())
		));

		for ($attempt = 0; $attempt < PluginBuilderHandler::MAX_AUTO_RETRIES; $attempt++) {
			$useFixMode = $fixModeOnly || ! empty($review['blocking']);

			if ($useFixMode) {
				$this->log($log, sprintf('retry ATTEMPT %d/%d START — mode=fix', $attempt + 1, PluginBuilderHandler::MAX_AUTO_RETRIES));
				$repairPrompt = PluginBuilderHandler::buildRepairPrompt($description, $generated, $ledger, $prd);
				$generated    = $this->generator->generateRepair($repairPrompt);
			} else {
				$this->log($log, sprintf('retry ATTEMPT %d/%d START — mode=regen', $attempt + 1, PluginBuilderHandler::MAX_AUTO_RETRIES));
				$retryDescription = PluginBuilderHandler::buildRetryDescription($description, $review, $ledger);
				$generated        = $this->generator->generate($retryDescription, array(), $includeSiteContext, $prd);
			}

			$review = $this->reviewer->review(
				array_merge(
					$generated,
					array(
						'description'     => $description,
						'library_version' => $libraryVersion,
					)
				)
			);

			$ledger->addFromReview($review, $attempt + 1);

			$this->log($log, sprintf(
				'retry ATTEMPT %d review — confidence=%d blocking=%s ledger=%d codes=[%s]',
				$attempt + 1,
				(int) ( $review['confidence'] ?? 0 ),
				! empty($review['blocking']) ? 'yes' : 'no',
				$ledger->countEntries(),
				implode(',', $ledger->getLastFatalCodes())
			));

			if ($ledger->isStuckRepeatingFatals()) {
				$this->log($log, 'retry STUCK — codes=[' . implode(',', $ledger->getLastFatalCodes()) . ']');
				return array($generated, $review, true, true, $ledger);
			}

			if (! PluginBuilderHandler::reviewNeedsRetry($review)) {
				$this->log($log, 'retry ATTEMPT ' . ( $attempt + 1 ) . ' PASS — stopping retries');
				break;
			}
		}

		return array($generated, $review, true, false, $ledger);
	}

	private function log( ?callable $log, string $message ): void {
		if ($log !== null) {
			$log($message);
		}
	}
}
