<?php

declare(strict_types=1);

namespace Butschster\ContextGenerator\Config\Registry;

/**
 * Container for all registries
 */
final class ConfigRegistry implements \JsonSerializable
{
    /** @var array<string, RegistryInterface> */
    private array $registries = [];

    public function __construct(
        private readonly ?string $schema = null,
    ) {}

    /**
     * Register a new registry
     */
    public function register(RegistryInterface $registry): self
    {
        $this->registries[$registry->getType()] = $registry;

        return $this;
    }

    /**
     * Check if a registry with the given type exists
     */
    public function has(string $type): bool
    {
        return isset($this->registries[$type]);
    }

    /**
     * Get a registry by type
     *
     * @template T of RegistryInterface
     * @param class-string<T> $className Optional class name to validate the registry type
     * @return T
     *
     * @throws \InvalidArgumentException If the registry does not exist or is not of the expected type
     */
    public function get(string $type, string $className): RegistryInterface
    {
        if (!$this->has($type)) {
            throw new \InvalidArgumentException(\sprintf('Registry of type "%s" does not exist', $type));
        }

        $registry = $this->registries[$type];

        if (!$registry instanceof $className) {
            throw new \InvalidArgumentException(
                \sprintf('Registry of type "%s" is not an instance of "%s"', $type, $className),
            );
        }

        return $registry;
    }

    /**
     * Get all registered registries
     *
     * @return array<string, RegistryInterface>
     */
    public function all(): array
    {
        return $this->registries;
    }

    public function jsonSerialize(): array
    {
        $data = [
            '$schema' => $this->schema,
        ];

        foreach ($this->registries as $type => $registry) {
            $data[$type] = $registry;
        }

        return \array_filter(
            $data,
            static fn($value) => $value !== null && $value !== [],
        );
    }
}
