<?php
/**
 * Record Changes session.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Domain\Recording;

use ScheduledChanges\Domain\Shared\UtcTimestamp;
use ScheduledChanges\Domain\Shared\Uuid;

/**
 * Source-to-workspace recording session persisted in custom tables.
 */
final class RecordingSession {
	private ?int $id;
	private Uuid $uuid;
	private int $source_id;
	private ?int $workspace_id;
	private string $source_provider_id;
	private string $source_type;
	private string $source_reference;
	private string $workspace_provider_id;
	private string $workspace_type;
	private ?string $workspace_reference;
	/**
	 * @var array<string, mixed>
	 */
	private array $relation_map;
	/**
	 * @var array<string, mixed>
	 */
	private array $provider_map;
	/**
	 * @var array<string, mixed>
	 */
	private array $baseline_snapshot;
	/**
	 * @var array<string, mixed>
	 */
	private array $current_snapshot;
	/**
	 * @var array<string, mixed>
	 */
	private array $diff;
	/**
	 * @var array<string, mixed>
	 */
	private array $selected_changes;
	private int $owner_id;
	private string $status;
	private UtcTimestamp $expires_at;
	private UtcTimestamp $created_at;
	private UtcTimestamp $updated_at;
	private string $cleanup_status;
	/**
	 * @param array<string,mixed> $relation_map Source/workspace relationship map.
	 * @param array<string,mixed> $provider_map Provider clone and placeholder metadata.
	 * @param array<string,mixed> $baseline_snapshot Baseline canonical source snapshot.
	 * @param array<string,mixed> $current_snapshot Latest canonical workspace snapshot.
	 * @param array<string,mixed> $diff Current diff document.
	 * @param array<string,mixed> $selected_changes Selected change document.
	 */
	public function __construct(
		?int $id,
		Uuid $uuid,
		int $source_id,
		?int $workspace_id,
		array $relation_map,
		array $provider_map,
		array $baseline_snapshot,
		array $current_snapshot,
		array $diff,
		array $selected_changes,
		int $owner_id,
		string $status,
		UtcTimestamp $expires_at,
		UtcTimestamp $created_at,
		UtcTimestamp $updated_at,
		string $cleanup_status,
		string $source_provider_id = '',
		string $source_type = '',
		?string $source_reference = null,
		string $workspace_provider_id = '',
		string $workspace_type = '',
		?string $workspace_reference = null
	) {
		$this->id                    = $id;
		$this->uuid                  = $uuid;
		$this->source_provider_id    = $this->normalizeToken( $source_provider_id, '' );
		$this->source_type           = $this->normalizeToken( $source_type, '' );
		$this->source_reference      = $this->normalizeReference(
			$source_reference,
			$source_id > 0 && '' !== $this->source_type ? $this->source_type . ':' . $source_id : ''
		);
		$this->source_id             = $this->referenceId( $this->source_reference, $source_id );
		$this->workspace_provider_id = $this->normalizeToken( $workspace_provider_id, '' );
		$this->workspace_type        = $this->normalizeToken( $workspace_type, '' );
		$this->workspace_reference   = $this->normalizeNullableReference(
			$workspace_reference,
			null !== $workspace_id && '' !== $this->workspace_type ? $this->workspace_type . ':' . $workspace_id : null
		);
		$this->workspace_id          = null === $this->workspace_reference && null === $workspace_id
			? null
			: $this->referenceId( (string) $this->workspace_reference, (int) $workspace_id );
		$this->relation_map          = $relation_map;
		$this->provider_map          = $provider_map;
		$this->baseline_snapshot     = $baseline_snapshot;
		$this->current_snapshot      = $current_snapshot;
		$this->diff                  = $diff;
		$this->selected_changes      = $selected_changes;
		$this->owner_id              = $owner_id;
		$this->status                = $status;
		$this->expires_at            = $expires_at;
		$this->created_at            = $created_at;
		$this->updated_at            = $updated_at;
		$this->cleanup_status        = $cleanup_status;
		if ( null !== $this->id && $this->id <= 0 ) {
			throw new \InvalidArgumentException( 'Recording session ID must be positive.' );
		}

		if ( '' === $this->source_provider_id || '' === $this->source_type || $this->source_id <= 0 ) {
			throw new \InvalidArgumentException( 'Canonical recording source identity is required.' );
		}

		if (
			null !== $this->workspace_id
			&& ( $this->workspace_id <= 0 || '' === $this->workspace_provider_id || '' === $this->workspace_type )
		) {
			throw new \InvalidArgumentException( 'Canonical recording workspace identity is invalid.' );
		}

		if ( $this->owner_id < 0 ) {
			throw new \InvalidArgumentException( 'Recording owner ID cannot be negative.' );
		}

		if ( array() === $this->baseline_snapshot ) {
			throw new \InvalidArgumentException( 'Recording baseline snapshot is required.' );
		}
	}

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

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

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

	/**
	 * Compatibility alias for WooCommerce-era callers.
	 */
	public function sourceProductId(): int {
		return $this->source_id;
	}

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

	/**
	 * Compatibility alias for WooCommerce-era callers.
	 */
	public function shadowProductId(): ?int {
		return $this->workspace_id;
	}

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

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

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

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

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

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

	/**
	 * @return array<string,mixed>
	 */
	public function relationMap(): array {
		return $this->relation_map;
	}

	/**
	 * Compatibility alias for WooCommerce-era callers.
	 *
	 * @return array<string,mixed>
	 */
	public function variationMap(): array {
		return $this->relation_map;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function providerMap(): array {
		return $this->provider_map;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function baselineSnapshot(): array {
		return $this->baseline_snapshot;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function currentSnapshot(): array {
		return $this->current_snapshot;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function diff(): array {
		return $this->diff;
	}

	/**
	 * @return array<string,mixed>
	 */
	public function selectedChanges(): array {
		return $this->selected_changes;
	}

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

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

	public function expiresAt(): UtcTimestamp {
		return $this->expires_at;
	}

	public function createdAt(): UtcTimestamp {
		return $this->created_at;
	}

	public function updatedAt(): UtcTimestamp {
		return $this->updated_at;
	}

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

	private function normalizeToken( string $value, string $fallback ): string {
		$value = trim( $value );

		return '' === $value ? $fallback : $value;
	}

	private function normalizeReference( ?string $value, string $fallback ): string {
		$value = null === $value ? '' : trim( $value );

		if ( '' !== $value ) {
			return $value;
		}

		if ( '' === trim( $fallback ) ) {
			throw new \InvalidArgumentException( 'Recording source reference is required.' );
		}

		return $fallback;
	}

	private function normalizeNullableReference( ?string $value, ?string $fallback ): ?string {
		$value = null === $value ? '' : trim( $value );

		if ( '' !== $value ) {
			return $value;
		}

		return null === $fallback || '' === trim( $fallback ) ? null : $fallback;
	}

	private function referenceId( string $reference, int $fallback ): int {
		if ( 1 === preg_match( '/:([1-9][0-9]*)$/', $reference, $matches ) ) {
			return (int) $matches[1];
		}

		return $fallback;
	}
}
