from pathlib import Path

import yaml

from src.domain.app_blocks.canonical import (
    CanonicalCatalog,
    compile_canonical_catalog,
    compile_default_catalog,
    feature_pack_only_aliases,
)
from src.domain.app_blocks.errors import UnknownAppBlockError
from src.domain.app_blocks.schemas import AppBlockManifest
from src.domain.app_blocks.validation import validate_manifest

MANIFEST_DIR = Path(__file__).resolve().parent / "manifests"


def load_manifests(directory: Path | None = None) -> list[AppBlockManifest]:
    root = directory or MANIFEST_DIR
    manifests = []
    for path in sorted(root.glob("*.yaml")):
        data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
        manifests.append(validate_manifest(data))
    if root.resolve() == MANIFEST_DIR.resolve():
        compile_default_catalog(manifests)
    else:
        compile_canonical_catalog(manifests)
    return manifests


class AppBlockRegistry:
    def __init__(self, manifests: list[AppBlockManifest]):
        # In-memory callers must satisfy the same reference checks as YAML.
        validated = [validate_manifest(item.model_dump()) for item in manifests]
        self._canonical = compile_canonical_catalog(validated)
        self._by_id = {block.id: block.manifest for block in self._canonical.blocks}

    @property
    def canonical(self) -> CanonicalCatalog:
        return self._canonical

    def get(self, block_id: str) -> AppBlockManifest | None:
        if block_id in feature_pack_only_aliases():
            return None
        return self._by_id.get(block_id)

    def require(self, block_id: str) -> AppBlockManifest:
        manifest = self.get(block_id)
        if not manifest:
            raise UnknownAppBlockError(block_id)
        return manifest

    def implemented(self, block_id: str) -> bool:
        return self.require(block_id).implemented

    def list_family(self, family: str) -> list[AppBlockManifest]:
        return [item for item in self._by_id.values() if item.family == family]

    def providers_for(self, capability: str) -> list[AppBlockManifest]:
        return [
            manifest
            for manifest in self._by_id.values()
            if any(provided.id == capability for provided in manifest.provides)
        ]

    def all(self) -> list[AppBlockManifest]:
        return list(self._by_id.values())


def load_default_registry() -> AppBlockRegistry:
    return AppBlockRegistry(load_manifests())
