<?php
/**
 * Immutable operation target.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Domain\Operation;

use ScheduledChanges\Domain\Shared\Uuid;

/**
 * Identifies an existing or future provider-owned object.
 */
final class TargetDefinition {
	private Uuid $uuid;
	private string $reference;
	private ?int $targetId;
	private string $targetType;
	private string $subtype;
	private ?string $parentReference;
	private string $providerId;
	private ?string $baselineFingerprint;
	private int $executionOrder          = 0;
	private ?Uuid $creationOperationUuid = null;
	public function __construct(
		Uuid $uuid,
		string $reference,
		?int $targetId,
		string $targetType,
		string $subtype,
		?string $parentReference,
		string $providerId,
		?string $baselineFingerprint,
		int $executionOrder = 0,
		?Uuid $creationOperationUuid = null
	) {
		$this->uuid                  = $uuid;
		$this->reference             = $reference;
		$this->targetId              = $targetId;
		$this->targetType            = $targetType;
		$this->subtype               = $subtype;
		$this->parentReference       = $parentReference;
		$this->providerId            = $providerId;
		$this->baselineFingerprint   = $baselineFingerprint;
		$this->executionOrder        = $executionOrder;
		$this->creationOperationUuid = $creationOperationUuid;
		if ( '' === trim( $this->reference ) || strlen( $this->reference ) > 191 ) {
			throw new \InvalidArgumentException( 'Target references must contain 1 to 191 characters.' );
		}

		if ( null !== $this->targetId && $this->targetId <= 0 ) {
			throw new \InvalidArgumentException( 'Target object IDs must be positive.' );
		}

		if ( '' === trim( $this->targetType ) || '' === trim( $this->providerId ) ) {
			throw new \InvalidArgumentException( 'Target object type and provider are required.' );
		}

		if ( 1 !== preg_match( '/^[a-z][a-z0-9_-]*$/', $this->targetType ) ) {
			throw new \InvalidArgumentException( 'Target object types must be stable lowercase identifiers.' );
		}

		if ( 1 !== preg_match( '/^[a-z][a-z0-9-]*$/', $this->providerId ) ) {
			throw new \InvalidArgumentException( 'Target provider IDs must be lowercase slugs.' );
		}

		if ( null === $this->targetId && null === $this->creationOperationUuid ) {
			throw new \InvalidArgumentException( 'Future targets require a creation operation UUID.' );
		}

		if ( null !== $this->baselineFingerprint && 1 !== preg_match( '/^[0-9a-f]{64}$/', $this->baselineFingerprint ) ) {
			throw new \InvalidArgumentException( 'Target fingerprints must be SHA-256 hashes.' );
		}
	}

	public function uuid(): Uuid {
		return $this->uuid;
	}

	public function reference(): string {
		return $this->reference;
	}

	public function targetId(): ?int {
		return $this->targetId;
	}

	public function targetType(): string {
		return $this->targetType;
	}

	public function subtype(): string {
		return $this->subtype;
	}

	public function parentReference(): ?string {
		return $this->parentReference;
	}

	public function providerId(): string {
		return $this->providerId;
	}

	/**
	 * Returns the canonical provider-scoped identity stored by the lock manager.
	 */
	public function lockReference(): string {
		$identity = $this->providerId . ':' . $this->reference;

		return strlen( $identity ) <= 191
			? $identity
			: 'identity:sha256:' . hash( 'sha256', $identity );
	}

	public function baselineFingerprint(): ?string {
		return $this->baselineFingerprint;
	}

	public function executionOrder(): int {
		return $this->executionOrder;
	}

	public function creationOperationUuid(): ?Uuid {
		return $this->creationOperationUuid;
	}

	public function future(): bool {
		return null === $this->targetId;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function toArray(): array {
		$creation_operation_uuid = null === $this->creationOperationUuid
			? null
			: $this->creationOperationUuid->value();

		return array(
			'uuid'                    => $this->uuid->value(),
			'target_reference'        => $this->reference,
			'target_id'               => $this->targetId,
			'target_type'             => $this->targetType,
			'subtype'                 => $this->subtype,
			'parent_reference'        => $this->parentReference,
			'provider_id'             => $this->providerId,
			'baseline_fingerprint'    => $this->baselineFingerprint,
			'execution_order'         => $this->executionOrder,
			'creation_operation_uuid' => $creation_operation_uuid,
		);
	}

	/**
	 * @param array<string,mixed> $data Serialized target.
	 */
	public static function fromArray( array $data ): self {
		$creationUuid = $data['creation_operation_uuid'] ?? null;
		$reference    = $data['target_reference'] ?? '';
		$target_id    = $data['target_id'] ?? null;
		$target_type  = $data['target_type'] ?? '';
		$subtype      = $data['subtype'] ?? '';
		$parent       = $data['parent_reference'] ?? null;

		return new self(
			new Uuid( (string) ( $data['uuid'] ?? '' ) ),
			(string) $reference,
			null === $target_id ? null : (int) $target_id,
			(string) $target_type,
			(string) $subtype,
			null === $parent ? null : (string) $parent,
			(string) ( $data['provider_id'] ?? '' ),
			null === ( $data['baseline_fingerprint'] ?? null ) ? null : (string) $data['baseline_fingerprint'],
			(int) ( $data['execution_order'] ?? 0 ),
			null === $creationUuid ? null : new Uuid( (string) $creationUuid )
		);
	}
}
