<?php
/**
 * Recording plan compilation application service.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Application;

// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Application exceptions are escaped or mapped at delivery boundaries.

use ScheduledChanges\Domain\Launch\CreationSource;
use ScheduledChanges\Domain\Launch\Launch;
use ScheduledChanges\Domain\Launch\LaunchVersion;
use ScheduledChanges\Domain\Launch\OperationPlan;
use ScheduledChanges\Domain\Operation\HumanSummaryGenerator;
use ScheduledChanges\Domain\Operation\Operation;
use ScheduledChanges\Field\FieldRegistry;
use ScheduledChanges\Provider\ProviderRegistry;
use ScheduledChanges\Provider\ProviderRequirementSet;
use ScheduledChanges\Repository\LaunchRepository;
use ScheduledChanges\Repository\LaunchVersionRepository;
use ScheduledChanges\Validation\PlanValidator;
use ScheduledChanges\Validation\ValidationEnvironment;
use ScheduledChanges\Validation\WordPressValidationEnvironment;

/**
 * Owns shared recording compilation validation, plan creation, and persistence.
 */
final class RecordingPlanCompilationService {
	private ProviderRegistry $providers;
	private FieldRegistry $fields;
	private LaunchRepository $launches;
	private LaunchVersionRepository $versions;
	private ValidationEnvironment $environment;

	/**
	 * Creates the application-level compilation service.
	 */
	public function __construct(
		ProviderRegistry $providers,
		FieldRegistry $fields,
		LaunchRepository $launches,
		LaunchVersionRepository $versions,
		?ValidationEnvironment $environment = null
	) {
		$this->providers   = $providers;
		$this->fields      = $fields;
		$this->launches    = $launches;
		$this->versions    = $versions;
		$this->environment = $environment ?? new WordPressValidationEnvironment();
	}

	/**
	 * Compiles normalized selected recording intent into a new immutable launch version.
	 */
	public function compile(
		RecordingPlanCompilationRequest $request,
		RecordingOperationGraphCompiler $compiler
	): RecordingPlanCompilationResult {
		$draft        = $compiler->compileGraph( $request );
		$graph        = $draft->graph();
		$provider_ids = array_values(
			array_unique(
				array_map(
					static fn ( Operation $operation ): string => $operation->providerId(),
					$graph->operations()
				)
			)
		);
		$requirements = ProviderRequirementSet::captureIds( $this->providers, $provider_ids );
		$validation   = ( new PlanValidator( $this->providers, $this->fields, $this->environment ) )->validate(
			$graph,
			$requirements->toArray(),
			$request->actorId()
		);

		if ( ! $validation->isValid() ) {
			throw new \RuntimeException( 'Recorded launch validation failed: ' . wp_json_encode( $validation->toArray() ) );
		}

		$plan   = OperationPlan::create(
			$graph,
			$validation,
			$draft->compatibility(),
			$requirements->toArray(),
			( new HumanSummaryGenerator() )->summarize( $graph ),
			$draft->fingerprints()
		);
		$launch = Launch::draft(
			$request->launchName(),
			$request->launchDescription(),
			$request->actorId(),
			$request->timezone(),
			CreationSource::RECORDING
		);

		$launch_id  = $this->launches->create( $launch );
		$version_id = $this->versions->create( LaunchVersion::create( $launch_id, 1, $plan, $request->actorId() ) );

		return new RecordingPlanCompilationResult(
			$launch->uuid(),
			$launch_id,
			$version_id,
			$draft->selectedChangeCount()
		);
	}
}
