<?php

declare(strict_types=1);

namespace CliqThemes\UI\ORM\Form;

use ArrayIterator;
use IteratorAggregate;
use Traversable;
use CliqThemes\UI\Support\Form\FormSchema;

/**
 * Represents a lookup map of form field storage definitions keyed by dotted field paths.
 *
 * @implements IteratorAggregate<string,array<string,mixed>>
 */
final class FieldStorageMap implements IteratorAggregate
{
    /**
     * @var array<string,array<string,mixed>>
     */
    private array $map;

    private function __construct(array $map)
    {
        $this->map = $map;
    }

    /**
     * Builds a storage map from a full form schema definition.
     *
     * @param array<string,mixed> $form
     */
    public static function fromForm(array $form): self
    {
        $flattened = FormSchema::flattenFields($form);
        $map = [];

        foreach ($flattened as $field) {
            $path = (string) ($field['path'] ?? '');
            $storage = $field['storage'] ?? [];

            if ($path === '') {
                continue;
            }

            $map[$path] = self::normaliseStorage(is_array($storage) ? $storage : []);
        }

        return new self($map);
    }

    /**
     * @param array<string,mixed> $storage
     *
     * @return array<string,mixed>
     */
    private static function normaliseStorage(array $storage): array
    {
        if (! isset($storage['target']) || ! is_string($storage['target']) || $storage['target'] === '') {
            $storage['target'] = 'base';
        }

        return $storage;
    }

    /**
     * Returns the storage definition for the provided field path.
     *
     * @return array<string,mixed>
     */
    public function get(string $path): array
    {
        return $this->map[$path] ?? ['target' => 'base'];
    }

    /**
     * Checks whether the storage map contains the given field path.
     */
    public function has(string $path): bool
    {
        return array_key_exists($path, $this->map);
    }

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

    /**
     * @return Traversable<string,array<string,mixed>>
     */
    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->map);
    }
}
