<?php
/**
 * Field definition collection.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Field;

use ArrayIterator;
use Countable;
use IteratorAggregate;
use Traversable;

/**
 * Typed, de-duplicated collection of canonical field definitions.
 *
 * @implements IteratorAggregate<string,FieldDefinition>
 */
final class FieldCollection implements Countable, IteratorAggregate {
	/**
	 * @var array<string,FieldDefinition>
	 */
	private array $fields = array();

	/**
	 * @param iterable<FieldDefinition> $fields Initial fields.
	 */
	public function __construct( iterable $fields = array() ) {
		foreach ( $fields as $field ) {
			$this->add( $field );
		}
	}

	public function add( FieldDefinition $field ): void {
		$this->fields[ $field->id() ] = $field;
	}

	public function get( string $id ): ?FieldDefinition {
		return $this->fields[ $id ] ?? null;
	}

	public function has( string $id ): bool {
		return isset( $this->fields[ $id ] );
	}

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

	/**
	 * @return Traversable<string,FieldDefinition>
	 */
	public function getIterator(): Traversable {
		ksort( $this->fields );

		return new ArrayIterator( $this->fields );
	}

	/**
	 * @return array<int,array<string,mixed>>
	 */
	public function toArray(): array {
		return array_values(
			array_map(
				static fn ( FieldDefinition $field ): array => $field->toArray(),
				$this->fields
			)
		);
	}

	/**
	 * @param array<int,array<string,mixed>> $definitions Cached definitions.
	 */
	public static function fromArray( array $definitions ): self {
		$fields = array();

		foreach ( $definitions as $definition ) {
			$fields[] = FieldDefinition::fromArray( $definition );
		}

		return new self( $fields );
	}
}
