"""
Morphology registry — reconstructive root and inflection structure.

The registry is deliberately pure schema + stdlib. It does not compile
versors, import algebra, or participate in propagation. Its job is to make
LexicalEntry.morphology_id resolve to ordered morphological structure.
"""

from __future__ import annotations

import json
from pathlib import Path

from language_packs.schema import MorphologyEntry

_DATA_DIR = Path(__file__).parent.parent / "language_packs" / "data"


class MorphologyRegistry:
    """Immutable morphology lookup table for one language pack."""

    def __init__(self, entries: list[MorphologyEntry]) -> None:
        self._entries: tuple[MorphologyEntry, ...] = tuple(entries)
        self._by_id: dict[str, MorphologyEntry] = {}
        self._by_surface: dict[str, MorphologyEntry] = {}
        for entry in self._entries:
            if entry.morphology_id in self._by_id:
                raise ValueError(f"Duplicate morphology_id: {entry.morphology_id}")
            self._by_id[entry.morphology_id] = entry
            self._by_surface[entry.surface] = entry

    def __len__(self) -> int:
        return len(self._entries)

    @property
    def entries(self) -> tuple[MorphologyEntry, ...]:
        return self._entries

    def get(self, morphology_id: str) -> MorphologyEntry | None:
        """Return an entry by morphology_id, or None if absent."""
        return self._by_id.get(morphology_id)

    def for_surface(self, surface: str) -> MorphologyEntry | None:
        """Return an entry by surface form, or None if absent."""
        return self._by_surface.get(surface)

    def require(self, morphology_id: str) -> MorphologyEntry:
        """Return an entry by morphology_id, raising if the link is broken."""
        entry = self.get(morphology_id)
        if entry is None:
            raise KeyError(f"Morphology id '{morphology_id}' not in registry.")
        return entry


def _parse_entry(payload: dict) -> MorphologyEntry:
    return MorphologyEntry(
        morphology_id=payload["morphology_id"],
        surface=payload["surface"],
        lemma=payload["lemma"],
        language=payload["language"],
        root=payload.get("root"),
        prefix_chain=tuple(payload.get("prefix_chain", [])),
        stem=payload.get("stem"),
        inflection=dict(payload.get("inflection", {})),
        suffix_chain=tuple(payload.get("suffix_chain", [])),
    )


def load_morphology(pack_id: str) -> MorphologyRegistry:
    """
    Load MorphologyEntry records from language_packs/data/<pack_id>/morphology.jsonl.

    Packs without morphology data return an empty registry; packs that set
    LexicalEntry.morphology_id are validated by the compiler against this
    registry.
    """
    morphology_path = _DATA_DIR / pack_id / "morphology.jsonl"
    if not morphology_path.exists():
        return MorphologyRegistry([])

    entries: list[MorphologyEntry] = []
    for line in morphology_path.read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if line:
            entries.append(_parse_entry(json.loads(line)))
    return MorphologyRegistry(entries)
