<?php
/**
 * Resolved field catalog.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Field;

// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception messages are escaped at UI boundaries.

use ScheduledChanges\Provider\FieldProvider;
use RuntimeException;

/**
 * Resolved definitions, write owners, and ownership diagnostics for a context.
 */
final class FieldCatalog {
	private FieldCollection $fields;
	/**
	 * @var array<string, FieldProvider>
	 */
	private array $owners;
	/**
	 * @var OwnershipCollision[]
	 */
	private array $collisions;
	/**
	 * @param array<string,FieldProvider> $owners Resolved providers keyed by field ID.
	 * @param OwnershipCollision[]        $collisions Ownership diagnostics.
	 */
	public function __construct( FieldCollection $fields, array $owners, array $collisions ) {
		$this->fields     = $fields;
		$this->owners     = $owners;
		$this->collisions = $collisions;
	}

	public function fields(): FieldCollection {
		return $this->fields;
	}

	public function owner( string $fieldId ): ?FieldProvider {
		return $this->owners[ $fieldId ] ?? null;
	}

	public function writableOwner( string $fieldId ): FieldProvider {
		foreach ( $this->collisions as $collision ) {
			if ( $collision->fieldId() === $fieldId && $collision->blocking() ) {
				throw new RuntimeException( 'Field ownership is unresolved for ' . $fieldId . '.' );
			}
		}

		$owner = $this->owner( $fieldId );
		$field = $this->fields->get( $fieldId );

		if ( null === $owner || null === $field || ! $field->writable() ) {
			throw new RuntimeException( 'No writable provider owns ' . $fieldId . '.' );
		}

		return $owner;
	}

	/**
	 * @return OwnershipCollision[]
	 */
	public function collisions(): array {
		return $this->collisions;
	}

	public function hasBlockingCollisions(): bool {
		foreach ( $this->collisions as $collision ) {
			if ( $collision->blocking() ) {
				return true;
			}
		}

		return false;
	}
}
