"""
mereoloji.types — Core data types for the Mereology Lens (Lens #2).

Implements the mereological relation from AGENTS.md Appendix C.5:
  Mereolojik (⊏) — Strict partial order: Part-of relation
  CEM M1–M5 + teleological T1–T5

Core types:
  - Telos       — Purpose/function of a component
  - Part        — An identifiable component with telos and holographic seed
  - Whole       — A composed structure with integration principle (AX5)
  - PartOfEdge  — A directed ⊏ relation between Part and Whole

Governing axioms enforced at construction:
  AX5:  Hayat (integration prerequisite) — whole needs integrating principle
  AX12: Vahidiyet — parts unified at system level
  AX13: Ehadiyet — each part reflects the whole
  AX27: Causes are transparent vessels — parts mediate, don't originate
  AX29: Nizam ∧ İntizam — both quantitative + qualitative order
  AX37: Holographic structure — ∀P ⊂ W: Structure(P) ≅ Structure(W)
  KV₆:  Holographic seed omnipresence — every part has seed > 0
  T10:  Severed cause impossibility — parts can't function detached
"""

from dataclasses import dataclass, field
from typing import Optional
import json


# ---------------------------------------------------------------------------
# Telos — Purpose/Function
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class Telos:
    """Purpose or function of a component in a mereological structure.

    Per framework teleological constraints T1–T5:
      T1: Every part has a telos
      T3: Purposes form a hierarchy (local → compositional → global)

    Attributes:
        name:        Purpose identifier (e.g. "photosynthesis", "data_validation")
        level:       "local" | "compositional" | "global"
        description: Optional human-readable description
    """
    name: str
    level: str  # "local" | "compositional" | "global"
    description: str = ""

    def __post_init__(self):
        if self.level not in ("local", "compositional", "global"):
            raise ValueError(
                f"Telos level must be 'local', 'compositional', or 'global', "
                f"got '{self.level}'"
            )
        if not self.name:
            raise ValueError("Telos must have a non-empty name (T1)")


# ---------------------------------------------------------------------------
# Part — Identifiable component
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class Part:
    """An identifiable component in a mereological structure.

    Per AGENTS.md §2.11 (AX37) and Appendix C.5:
      - Each part carries a holographic seed (KV₆: must be > 0)
      - Each part has a telos (T1)
      - Parts are transparent vessels (AX27) — they mediate, don't originate

    Attributes:
        name:             Unique part identifier
        telos:            Purpose of this part (T1: mandatory)
        holographic_seed: Degree to which this part mirrors the whole (KV₆: > 0)
        is_transparent:   AX27: part mediates but does not originate (default True)
        esma_refs:        Names (from kavram_sozlugu) this part manifests — READ-ONLY ref
    """
    name: str
    telos: Telos
    holographic_seed: float = 0.01  # KV₆: must be > 0
    is_transparent: bool = True     # AX27: default = transparent vessel
    esma_refs: tuple[str, ...] = ()  # Name references (cross-lens, read-only)

    def __post_init__(self):
        if not self.name:
            raise ValueError("Part must have a non-empty name")
        # KV₆: holographic seed must be strictly positive
        if self.holographic_seed <= 0:
            object.__setattr__(self, 'holographic_seed', 0.001)


# ---------------------------------------------------------------------------
# Whole — Composed structure
# ---------------------------------------------------------------------------

@dataclass
class Whole:
    """A composed structure with parts, integration principle, and global telos.

    Per AGENTS.md:
      AX5:  Integration prerequisite — must have an integrating principle
      AX12: Vahidiyet — parts held under global unity
      AX29: Both quantitative (nizam) and qualitative (intizam) order required
      T5:   Whole's telos is irreducible to sum of part-teloi

    Attributes:
        name:                    Unique whole identifier
        telos:                   Global telos of this whole (T5)
        integration_principle:   What binds parts into unity (AX5: mandatory)
        nizam_score:             Quantitative order [0, 1) — AX29
        intizam_score:           Qualitative order [0, 1) — AX29
    """
    name: str
    telos: Telos
    integration_principle: str  # AX5: mandatory — what integrates parts
    nizam_score: float = 0.5   # AX29: quantitative regularity
    intizam_score: float = 0.5  # AX29: qualitative purposiveness

    def __post_init__(self):
        if not self.name:
            raise ValueError("Whole must have a non-empty name")
        if not self.integration_principle:
            raise ValueError(
                f"Whole '{self.name}': integration_principle is required (AX5 — "
                f"without Hayat, attributes are inert)"
            )
        # Clamp scores to [0, 1)
        self.nizam_score = max(0.0, min(self.nizam_score, 0.9999))
        self.intizam_score = max(0.0, min(self.intizam_score, 0.9999))

    @property
    def cosmic_order(self) -> float:
        """AX29: CosmicOrder = Nizam ∧ İntizam.

        Multiplicative — if either is zero, total order collapses (AX52 pattern).
        """
        combined = self.nizam_score * self.intizam_score
        return min(combined, 0.9999)

    def to_dict(self) -> dict:
        return {
            "name": self.name,
            "telos": {"name": self.telos.name, "level": self.telos.level,
                      "description": self.telos.description},
            "integration_principle": self.integration_principle,
            "nizam_score": self.nizam_score,
            "intizam_score": self.intizam_score,
        }


# ---------------------------------------------------------------------------
# PartOfEdge — The ⊏ relation
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class PartOfEdge:
    """A directed part-of relation: part ⊏ whole.

    Per AGENTS.md Appendix C.5:
      CEM M1: Irreflexivity — part_name != whole_name
      CEM M2: Asymmetry — enforced at graph level (MereologicalStructure)
      CEM M3: Transitivity — enforced at graph level

    Attributes:
        part_name:  Name of the part
        whole_name: Name of the whole it belongs to
    """
    part_name: str
    whole_name: str

    def __post_init__(self):
        # CEM M1: Irreflexivity — nothing is a proper part of itself
        if self.part_name == self.whole_name:
            raise ValueError(
                f"CEM M1 violation: '{self.part_name}' cannot be a proper part "
                f"of itself (irreflexivity)"
            )
