"""
kavram_sozlugu.registry — Core registry for the Ontology Lens (Lens #1).

Implements the KavramSozlugu class: the central concept dictionary that
maps concepts to their constitutive Names and verifies kavaid compliance.

Per AGENTS.md §4.1:
  Lens: Ontoloji | Faculty: Akil | Instrument: kavram_sozlugu
  Domain: Concept ontology / Name mapping

Per §4.2: Each lens is independent (KV7), produces convergence score in [0, 1).
Per §6.4: Katman 1 = ONTOLOGY — concept vocabulary, Name mapping.

Standalone functions implement Appendix C predicates:
  tecelli(), istinad(), delalet(), mana_harfi(), mana_ismi()
"""

from kavram_sozlugu.types import Sifat, Isim, Tecelli, Kavram
from kavram_sozlugu.data import SIFAT_SEBA, ESMA_UL_HUSNA, VALID_SIFAT_NAMES
import warnings


class KavramSozlugu:
    """Ontology Lens — Concept Dictionary (Lens #1 of 7).

    Registry for Names (S_Isim) and Concepts (S_Kavram), providing:
      - Concept registration with axiom enforcement
      - Hakikat/Mahiyet lookups (AX17, AX18)
      - Kavaid verification (KV1, KV8, AX19, AX22)
      - Lens-level convergence score (§4.2)
      - Ontological stack mapping (§1.5)

    Pre-loaded with SIFAT_SEBA (7 attributes) and ESMA_UL_HUSNA (99 Names).
    """

    def __init__(self, preload: bool = True):
        """Initialize the registry.

        Args:
            preload: If True (default), pre-load the 7 Sifat and 99 Names.
                     Set False for empty registry (useful in tests).
        """
        self._sifatlar: dict[str, Sifat] = {}
        self._isimler: dict[str, Isim] = {}
        self._kavramlar: dict[str, Kavram] = {}

        if preload:
            for sifat in SIFAT_SEBA.values():
                self._sifatlar[sifat.name] = sifat
            for isim in ESMA_UL_HUSNA.values():
                self._isimler[isim.name] = isim

    # ------------------------------------------------------------------
    # Registration
    # ------------------------------------------------------------------

    def register_isim(self, isim: Isim) -> None:
        """Register a Name in the ontology.

        Validates that all Sifat references exist in the registry.
        """
        unknown_sifat = isim.sifat - set(self._sifatlar.keys())
        if unknown_sifat:
            warnings.warn(
                f"Isim '{isim.name}' references unknown Sifat: {unknown_sifat}. "
                f"Register the Sifat first for full ontological tracing."
            )
        self._isimler[isim.name] = isim

    def register_kavram(self, kavram: Kavram) -> None:
        """Register a Concept in the ontology.

        Validates:
          - All Tecelli.isim references exist in the Name registry
          - AX22: All degrees < ekmel for their respective Names
          - KV1/KV8/AX19 already enforced by Kavram.__post_init__
        """
        for t in kavram.hakikat:
            isim = self._isimler.get(t.isim)
            if isim is None:
                warnings.warn(
                    f"Kavram '{kavram.name}' references unknown Name '{t.isim}'. "
                    f"Register the Name first for full ontological tracing."
                )
            elif t.derece >= isim.ekmel:
                raise ValueError(
                    f"Kavram '{kavram.name}': Tecelli degree {t.derece} for "
                    f"Name '{t.isim}' >= ekmel {isim.ekmel} (AX22 violation)"
                )
        self._kavramlar[kavram.name] = kavram

    # ------------------------------------------------------------------
    # Lookups
    # ------------------------------------------------------------------

    def get_isim(self, name: str) -> Isim | None:
        """Look up a Name by string. Returns None if not found."""
        return self._isimler.get(name)

    def get_kavram(self, name: str) -> Kavram | None:
        """Look up a Concept by string. Returns None if not found."""
        return self._kavramlar.get(name)

    def list_isimler(self) -> list[str]:
        """Return all registered Name strings."""
        return list(self._isimler.keys())

    def list_kavramlar(self) -> list[str]:
        """Return all registered Concept strings."""
        return list(self._kavramlar.keys())

    # ------------------------------------------------------------------
    # AX17: Hakikat — Structural Identity
    # ------------------------------------------------------------------

    def hakikat(self, concept_name: str) -> tuple[Tecelli, ...]:
        """Return the structural identity of a concept (AX17).

        Hakikat(x) = {n ∈ S_Isim | Tecelli(n,x) > 0}
        """
        kavram = self._kavramlar.get(concept_name)
        if kavram is None:
            raise KeyError(f"Concept '{concept_name}' not in registry")
        return kavram.hakikat

    # ------------------------------------------------------------------
    # AX18: Mahiyet — Surface Description (Shadow)
    # ------------------------------------------------------------------

    def mahiyet(self, concept_name: str) -> str:
        """Return the surface description of a concept (AX18).

        Mahiyet(x) = Shadow(Hakikat(x)) — always derivative.
        """
        kavram = self._kavramlar.get(concept_name)
        if kavram is None:
            raise KeyError(f"Concept '{concept_name}' not in registry")
        return kavram.mahiyet

    # ------------------------------------------------------------------
    # Kavaid Verification
    # ------------------------------------------------------------------

    def verify_kv1(self, kavram: Kavram) -> bool:
        """KV1: Concept has valid dual classification (harfi/ismi)."""
        return kavram.dual_class in ("harfi", "ismi")

    def verify_kv8(self, kavram: Kavram) -> bool:
        """KV8: Concept is grounded in >= 1 Name."""
        return len(kavram.hakikat) >= 1

    def verify_ax19(self, kavram: Kavram) -> bool:
        """AX19: Living concept has >= 20 Names."""
        if not kavram.is_living:
            return True  # Non-living: constraint doesn't apply
        return len(kavram.hakikat) >= 20

    def verify_ax22(self, kavram: Kavram) -> bool:
        """AX22: All Tecelli degrees < ekmel for their respective Names."""
        for t in kavram.hakikat:
            isim = self._isimler.get(t.isim)
            if isim and t.derece >= isim.ekmel:
                return False
        return True

    def verify_all_kavaid(self, kavram: Kavram) -> list[tuple[str, bool]]:
        """Check all applicable kavaid for one concept.

        Returns list of (constraint_name, passed) tuples.
        """
        results = [
            ("KV1 (harfi/ismi classification)", self.verify_kv1(kavram)),
            ("KV8 (>= 1 Name grounding)", self.verify_kv8(kavram)),
            ("AX19 (living >= 20 Names)", self.verify_ax19(kavram)),
            ("AX22 (degree < ekmel)", self.verify_ax22(kavram)),
        ]
        return results

    # ------------------------------------------------------------------
    # §4.2: Convergence Score
    # ------------------------------------------------------------------

    def yakinlasma(self) -> float:
        """Lens-level convergence score in [0, 1).

        Measures how well registered concepts satisfy ontology constraints.
        Average kavaid satisfaction across all concepts, clamped < 1.0 (KV4).

        Returns 0.0 if no concepts are registered.
        """
        if not self._kavramlar:
            return 0.0

        total_score = 0.0
        for kavram in self._kavramlar.values():
            checks = self.verify_all_kavaid(kavram)
            passed = sum(1 for _, v in checks if v)
            total_score += passed / len(checks)

        score = total_score / len(self._kavramlar)
        # KV4: convergence bound — always strictly < 1.0
        return min(score, 0.9999)

    # ------------------------------------------------------------------
    # §1.5: Ontological Stack (4-level abstraction funnel)
    # ------------------------------------------------------------------

    def ont_stack(self, concept_name: str) -> dict:
        """Map concept through the 4-level abstraction funnel.

        OntStack4 = (S_Fiil, S_Isim, S_Sifat, S_Zat)
        Convergence: infinity -> ~1001 -> 7 -> 1

        Returns dict with keys: fiiller, esma, sifat, zat
        """
        kavram = self._kavramlar.get(concept_name)
        if kavram is None:
            raise KeyError(f"Concept '{concept_name}' not in registry")

        # Level 1 (Esma): Names constituting this concept
        esma = set()
        for t in kavram.hakikat:
            esma.add(t.isim)

        # Level 2 (Sifat): Attributes those Names derive from
        sifat = set()
        for t in kavram.hakikat:
            isim = self._isimler.get(t.isim)
            if isim:
                sifat.update(isim.sifat)

        # Level 0 (Fiiller): Observable actions — domain-specific
        # Requires Katman 0 (DATA layer), returns concept name as placeholder
        # Level 3 (Zat): Always converges to 1
        return {
            "fiiller": [concept_name],  # Placeholder for action-level data
            "esma": sorted(esma),
            "sifat": sorted(sifat),
            "zat": 1,  # §1.5: Always converges to single source
        }


# ---------------------------------------------------------------------------
# Standalone Predicate Functions (Appendix C.3)
# ---------------------------------------------------------------------------

def tecelli_degree(isim_name: str, kavram: Kavram) -> float:
    """Return the manifestation degree of a Name in a concept.

    AX21: Tecelli(n,x) ∈ R>=0 (continuous, not binary)
    Returns 0.0 if the Name does not manifest in this concept.
    """
    for t in kavram.hakikat:
        if t.isim == isim_name:
            return t.derece
    return 0.0


def istinad(kavram: Kavram, isim_name: str) -> bool:
    """AX20: Concept rests upon Name — True if Name is in hakikat(kavram).

    Istinad(c, n) -> Bool: concept c rests upon Name n.
    """
    return any(t.isim == isim_name for t in kavram.hakikat)


def delalet(kavram: Kavram, isim_name: str) -> bool:
    """Work/trace indicates Name — True if concept's hakikat includes this Name.

    Delalet(w, n) -> Bool: work w indicates Name n.
    For the ontology lens, this is equivalent to istinad.
    """
    return istinad(kavram, isim_name)


def mana_harfi(kavram: Kavram) -> frozenset[str]:
    """KV1 harfi: set of Names the concept points to (other-referential).

    ManaHarfi(c) -> P+(S_Isim): the Names this concept refers beyond itself to.
    Only meaningful when dual_class == 'harfi'.
    """
    if kavram.dual_class != "harfi":
        return frozenset()
    return frozenset(t.isim for t in kavram.hakikat)


def mana_ismi(kavram: Kavram) -> str:
    """KV1 ismi: self-referential meaning.

    ManaIsmi(c) -> S_Kavram: the concept's self-referential meaning.
    Returns the mahiyet (surface description) when in ismi mode.
    """
    return kavram.mahiyet if kavram.dual_class == "ismi" else kavram.name
