<?php
/**
 * Request-local execution state.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Execution;

use ScheduledChanges\Domain\Operation\OperationGraph;

/**
 * Resolves existing and newly created target references during one run.
 */
final class ExecutionState {
	/**
	 * @var array<string,int>
	 */
	private array $objectIds = array();

	/**
	 * @var array<string,mixed>
	 */
	private array $desiredValues = array();

	/**
	 * @var array<string,array<string,mixed>>
	 */
	private array $conflicts = array();

	public function __construct( OperationGraph $graph ) {
		foreach ( $graph->targets() as $target ) {
			if ( null !== $target->targetId() ) {
				$this->objectIds[ $target->reference() ] = $target->targetId();
			}
		}
	}

	public function resolve( string $target_reference ): ?int {
		return $this->objectIds[ $target_reference ] ?? null;
	}

	public function register( string $target_reference, int $object_id ): void {
		if ( $object_id <= 0 ) {
			throw new \InvalidArgumentException( 'Resolved target object IDs must be positive.' );
		}

		$existing = $this->objectIds[ $target_reference ] ?? null;
		if ( null !== $existing && $existing !== $object_id ) {
			throw new \RuntimeException( 'Target reference resolved to conflicting object IDs.' );
		}

		$this->objectIds[ $target_reference ] = $object_id;
	}

	/**
	 * @param mixed $value
	 */
	public function setDesiredValue( string $operation_uuid, $value ): void {
		$this->desiredValues[ $operation_uuid ] = $value;
	}

	/**
	 * @return mixed
	 */
	public function desiredValue( \ScheduledChanges\Domain\Operation\Operation $operation ) {
		return array_key_exists( $operation->uuid()->value(), $this->desiredValues )
			? $this->desiredValues[ $operation->uuid()->value() ]
			: $operation->after();
	}

	/**
	 * @param array<string,mixed> $details Conflict decision details.
	 */
	public function recordConflict( string $operation_uuid, array $details ): void {
		$this->conflicts[ $operation_uuid ] = $details;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function conflict( string $operation_uuid ): array {
		return $this->conflicts[ $operation_uuid ] ?? array();
	}
}
