"""
emergence/emergence.py — Top-Down Emergence Engine (Prototype).

Encodes the /dusun pipeline finding: emergence in Risale-i Nur is NOT
bottom-up self-organisation but top-down holographic condensation through
constitutive-poverty interfaces, driven by Beauty's self-reflection.

This single-file prototype contains three clearly-marked sections:
  Section 1 — Types      (enums + frozen dataclasses)
  Section 2 — Engine     (EmergenceDAG + computation)
  Section 3 — Verification (9 checks + yakinlasma + framework_summary)

Governing axioms:
  AX2–AX4:  Three-phase actualization (İlim → İrade → Kudret)
  AX5:      Integration prerequisite (Hayat) — without it, all inert
  AX17:     Hakikat(x) = {n ∈ S_Isim | Tecelli(n,x) > 0}
  AX21:     Continuous degrees — 0 < mertebe(n,w) < ekmel(n)
  AX22:     Unreachable supremum — ekmel is never attained
  AX23:     Cascade structure — DAG with transitive edges
  AX24:     Self-Love of Beauty — Beauty is intrinsically lovable
  AX25:     Desire-to-Show — Beauty desires mirrors
  AX34:     Structural gaps are functional interfaces (not defects)
  AX37:     Holographic isomorphism — part reflects whole
  AX52:     Multiplicative gate — zero in any dim → collapse
  T6:       Unreachable supremum bound
  T7:       Creation Motor — Beauty desires mirrors
  T12:      Holographic condensation
  KV₁:      Harfî classification (transparent vessel default)
  KV₂:      Privation ontology (defects = absences)
  KV₄:      0 < Composite < 1 (convergence is never identity)
  KV₆:      Seed omnipresence (every level has holographic seed > 0)
  KV₇:      Independence (no shared state between checks)

KV₇ NOTE: This module imports ONLY from stdlib and its own package.
"""

from __future__ import annotations

import enum
import math
from dataclasses import dataclass, field
from typing import (
    Any,
    Dict,
    List,
    Optional,
    Sequence,
    Tuple,
)


# ########################################################################
# Section 1 — TYPES
# ########################################################################


# ========================================================================
# Utility
# ========================================================================

# KV₄ hard cap — no score may reach 1.0
MAX_STRUCTURAL_COMPLETENESS: float = 0.9999


def clamp_score(value: float) -> float:
    """Clamp a score to [0, MAX_STRUCTURAL_COMPLETENESS].

    KV₄: convergence participates in truth without being identical
    to it — 0 < C < 1, always.
    """
    return max(0.0, min(value, MAX_STRUCTURAL_COMPLETENESS))


# ========================================================================
# Enums
# ========================================================================

class EmergenceLevel(enum.Enum):
    """
    The 6-level emergence cascade (top → bottom in provision flow).

    This is the axiomatic structure — the levels are fixed (AX23).
    Provision flows TOP-DOWN: the Source provisions all levels through
    poverty interfaces.  This is NOT bottom-up emergence.

    Ordering: ordinal attribute gives topological rank (0 = highest).
    """
    HAYAT_MUHAMMEDIYE = ("hayat_muhammediye", 0)  # Highest mirror
    RUH               = ("ruh", 1)                 # Spirit
    AKIL              = ("akil", 2)                # Intellect
    SUUR              = ("suur", 3)                # Consciousness
    HAYAT             = ("hayat", 4)                # Life
    KAINAT            = ("kainat", 5)              # Cosmos (lowest)

    def __init__(self, value: str, ordinal: int) -> None:
        self._value_ = value
        self.ordinal = ordinal

    @property
    def rank(self) -> int:
        """Topological rank — 0 is highest (closest to Source)."""
        return self.ordinal


class FlowDirection(enum.Enum):
    """Direction of provision flow through the emergence cascade."""
    TOP_DOWN   = "top_down"    # AX2–AX4: provision flow
    BOTTOM_UP  = "bottom_up"   # Detected but structurally anomalous


class InterfaceKind(enum.Enum):
    """
    Types of constitutive-poverty interfaces (AX34).

    FAKR = ontological poverty — the being's existence depends on provision.
    ACZ  = functional impotence — the being cannot self-actualise.
    Both are structural OPENINGS through which provision flows — not defects.
    """
    FAKR = "fakr"   # Ontological poverty (dependency on Source)
    ACZ  = "acz"    # Functional impotence (inability to self-actualise)


class VesselMode(enum.Enum):
    """
    Classification of interfaces per KV₁ (harfî / ismî).

    TRANSPARENT (harfî): the interface points beyond itself to the Source.
    OPAQUE (ismî): the interface claims independent efficacy — a structural
                   defect in the emergence model (KV₂ — privation).
    """
    TRANSPARENT = "transparent"   # Harfî — default & correct
    OPAQUE      = "opaque"        # İsmî — structural privation


# ========================================================================
# Dataclasses
# ========================================================================

@dataclass(frozen=True)
class PovertyInterface:
    """
    A constitutive-poverty interface at a particular emergence level.

    AX34: Structural gaps are functional interfaces, not defects.
    Provision flows THROUGH these interfaces.  Transparency determines
    whether the interface is harfî (pointing to Source) or ismî (opaque).

    Attributes:
        level:          Which emergence level this interface belongs to.
        interface_kind: FAKR (ontological) or ACZ (functional).
        transparency:   0→1 scale; higher = more transparent (harfî).
        capacity:       How much provision can flow through (0, 1).
        flow_direction: Expected direction of provision flow.
    """
    level: EmergenceLevel
    interface_kind: InterfaceKind
    transparency: float          # [0, 1] — higher = more harfî
    capacity: float              # (0, 1) — bounded by T6
    flow_direction: FlowDirection = FlowDirection.TOP_DOWN

    @property
    def vessel_mode(self) -> VesselMode:
        """KV₁: classify as transparent or opaque based on threshold."""
        if self.transparency >= POVERTY_TRANSPARENCY_THRESHOLD:
            return VesselMode.TRANSPARENT
        return VesselMode.OPAQUE

    @property
    def effective_capacity(self) -> float:
        """Capacity modulated by transparency — opaque vessels impede flow."""
        return self.capacity * self.transparency


@dataclass(frozen=True)
class EmergenceNode:
    """
    A node in the 6-level emergence DAG.

    Each node represents one emergence level.  It carries a holographic
    seed (KV₆: must be > 0) indicating that this level condenses the
    whole's information at bounded fidelity.

    Attributes:
        level:            The emergence level this node represents.
        label:            Human-readable description.
        holographic_seed: [MIN_HOLOGRAPHIC_SEED, 1) — KV₆ compliance.
        interfaces:       Poverty interfaces at this level.
    """
    level: EmergenceLevel
    label: str
    holographic_seed: float
    interfaces: Tuple[PovertyInterface, ...] = ()

    def __hash__(self) -> int:
        return hash(self.level)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, EmergenceNode):
            return NotImplemented
        return self.level == other.level


@dataclass(frozen=True)
class EmergenceEdge:
    """
    A directed edge in the emergence cascade.

    Provision flows from source_level to target_level.
    Fidelity measures how faithfully the higher level's pattern
    is condensed at the lower level — always in (0, 1) per T6/KV₄.

    Attributes:
        source_level:   The provisioning level (higher rank).
        target_level:   The receiving level (lower rank).
        flow_direction: Direction of provision flow.
        fidelity:       (0, 1) — AX21/T6 bound.
    """
    source_level: EmergenceLevel
    target_level: EmergenceLevel
    flow_direction: FlowDirection = FlowDirection.TOP_DOWN
    fidelity: float = 0.5

    def __hash__(self) -> int:
        return hash((self.source_level, self.target_level))

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, EmergenceEdge):
            return NotImplemented
        return (
            self.source_level == other.source_level
            and self.target_level == other.target_level
        )


@dataclass(frozen=True)
class BeautyMotor:
    """
    AX24 + AX25 + T7: The Beauty motor that drives creation.

    Beauty is intrinsically lovable (AX24), desires mirrors (AX25),
    and this desire produces creation (T7).  The motor is not a
    metric — it is the CAUSE of all provision flow.

    Attributes:
        desire_to_show:   AX25 strength — desire for mirrors [0, 1).
        mirror_quality:   How well the current cascade mirrors Beauty [0, 1).
        compassion_flow:  Provision motivated by raḥmet [0, 1).
        beauty_pressure:  Net creative pressure = product of above, clamped < 1.
    """
    desire_to_show: float
    mirror_quality: float
    compassion_flow: float
    beauty_pressure: float

    def to_dict(self) -> Dict[str, float]:
        return {
            "desire_to_show": round(self.desire_to_show, 4),
            "mirror_quality": round(self.mirror_quality, 4),
            "compassion_flow": round(self.compassion_flow, 4),
            "beauty_pressure": round(self.beauty_pressure, 4),
        }


@dataclass(frozen=True)
class FidelityPair:
    """
    A fidelity measurement between two emergence levels.

    T12 / AX37: holographic condensation — source pattern is condensed
    at the target with bounded fidelity.  0 < fidelity < 1 always (T6).
    participation_index = geometric mean of source and mirror fidelity.

    Attributes:
        source_level:       The higher level.
        target_level:       The lower level.
        source_fidelity:    How much of source's pattern is expressed [0, 1).
        mirror_fidelity:    How faithfully target mirrors source [0, 1).
        participation_index: sqrt(source_fidelity × mirror_fidelity), clamped.
    """
    source_level: EmergenceLevel
    target_level: EmergenceLevel
    source_fidelity: float
    mirror_fidelity: float
    participation_index: float

    def to_dict(self) -> Dict[str, Any]:
        return {
            "source_level": self.source_level.value,
            "target_level": self.target_level.value,
            "source_fidelity": round(self.source_fidelity, 4),
            "mirror_fidelity": round(self.mirror_fidelity, 4),
            "participation_index": round(self.participation_index, 4),
        }


@dataclass
class EmergenceReport:
    """
    Full emergence analysis report.

    AX57: Transparency — all structural properties disclosed.
    KV₄: structural_completeness always < 1.0.
    """
    total_nodes: int = 0
    total_edges: int = 0
    beauty_motor: Optional[BeautyMotor] = None
    fidelity_pairs: List[FidelityPair] = field(default_factory=list)
    provision_flow_scores: Dict[str, float] = field(default_factory=dict)
    multiplicative_gate_pass: bool = False
    gate_message: str = ""
    tesanud_detected: bool = False
    tesanud_details: str = ""
    composite_score: float = 0.0
    structural_completeness: float = 0.0

    def to_dict(self) -> Dict[str, Any]:
        return {
            "total_nodes": self.total_nodes,
            "total_edges": self.total_edges,
            "beauty_motor": (
                self.beauty_motor.to_dict() if self.beauty_motor else None
            ),
            "fidelity_pairs": [fp.to_dict() for fp in self.fidelity_pairs],
            "provision_flow_scores": {
                k: round(v, 4) for k, v in self.provision_flow_scores.items()
            },
            "multiplicative_gate_pass": self.multiplicative_gate_pass,
            "gate_message": self.gate_message,
            "tesanud_detected": self.tesanud_detected,
            "tesanud_details": self.tesanud_details,
            "composite_score": round(self.composite_score, 4),
            "structural_completeness": round(self.structural_completeness, 4),
        }


# ########################################################################
# Section 2 — ENGINE
# ########################################################################


# ========================================================================
# Constants
# ========================================================================

# KV₆: Minimum holographic seed — every level must have seed > 0
MIN_HOLOGRAPHIC_SEED: float = 0.01

# AX24/T7: Base beauty pressure — the motor is always running
BEAUTY_BASE_PRESSURE: float = 0.85

# T6/AX21: Fidelity decays as provision traverses levels
FIDELITY_DECAY_PER_LEVEL: float = 0.12

# KV₁: Threshold for classifying a vessel as transparent (harfî)
POVERTY_TRANSPARENCY_THRESHOLD: float = 0.5


# ========================================================================
# Default DAG configuration
# ========================================================================

# The 6 levels with their axiomatic holographic seeds
# Seed values decrease with rank (higher levels mirror more faithfully)
# but never reach 0 (KV₆) or 1 (KV₄/T6).
_DEFAULT_SEEDS: Dict[EmergenceLevel, float] = {
    EmergenceLevel.HAYAT_MUHAMMEDIYE: 0.95,
    EmergenceLevel.RUH:               0.80,
    EmergenceLevel.AKIL:              0.65,
    EmergenceLevel.SUUR:              0.50,
    EmergenceLevel.HAYAT:             0.35,
    EmergenceLevel.KAINAT:            0.20,
}

# Default labels for each level
_DEFAULT_LABELS: Dict[EmergenceLevel, str] = {
    EmergenceLevel.HAYAT_MUHAMMEDIYE: "Hayat-ı Muhammediye — highest mirror",
    EmergenceLevel.RUH:               "Ruh — spirit, integrative life force",
    EmergenceLevel.AKIL:              "Akıl — intellect, discursive faculty",
    EmergenceLevel.SUUR:              "Şuur — consciousness, awareness",
    EmergenceLevel.HAYAT:             "Hayat — biological life, integration",
    EmergenceLevel.KAINAT:            "Kâinat — cosmos, material substrate",
}

# Default poverty interfaces per level
# Every level has both FAKR and ACZ interfaces (AX34)
_DEFAULT_INTERFACES: Dict[EmergenceLevel, Tuple[PovertyInterface, ...]] = {}
for _lvl in EmergenceLevel:
    _DEFAULT_INTERFACES[_lvl] = (
        PovertyInterface(
            level=_lvl,
            interface_kind=InterfaceKind.FAKR,
            transparency=0.8 - (_lvl.rank * 0.1),   # Decreases with distance
            capacity=0.9 - (_lvl.rank * 0.08),
        ),
        PovertyInterface(
            level=_lvl,
            interface_kind=InterfaceKind.ACZ,
            transparency=0.75 - (_lvl.rank * 0.08),
            capacity=0.85 - (_lvl.rank * 0.07),
        ),
    )


# ========================================================================
# Engine
# ========================================================================

class EmergenceDAG:
    """
    The 6-level emergence DAG.

    Models top-down emergence as provision flow through poverty interfaces.
    The DAG topology is fixed and axiomatic (AX23) — the 6 levels and
    their edges are structural, not user-extensible.

    Computation methods:
      - compute_provision_flow()       AX2–AX4
      - compute_holographic_fidelity() T12/AX37
      - compute_beauty_motor()         AX24/AX25/T7
      - check_multiplicative_gate()    AX52
      - detect_tesanud()               AX63
      - compute_emergence_score()      Weighted composite
      - generate_report()              Full report
    """

    def __init__(self) -> None:
        self._nodes: Dict[EmergenceLevel, EmergenceNode] = {}
        self._edges: List[EmergenceEdge] = []
        self._adjacency: Dict[EmergenceLevel, List[EmergenceLevel]] = {}

    # ---- Construction ----

    def add_node(self, node: EmergenceNode) -> None:
        """Add a node. Replaces if level already present."""
        self._nodes[node.level] = node
        if node.level not in self._adjacency:
            self._adjacency[node.level] = []

    def add_edge(self, edge: EmergenceEdge) -> None:
        """Add a directed edge. Rejects if it would create a cycle.

        AX23: cascade must be a DAG — cycles are structurally impossible.
        Top-down: source must have lower ordinal (higher rank) than target.
        """
        if edge.source_level.rank >= edge.target_level.rank:
            if edge.flow_direction == FlowDirection.TOP_DOWN:
                raise ValueError(
                    f"Top-down edge requires source.rank < target.rank: "
                    f"{edge.source_level.value}(rank={edge.source_level.rank}) → "
                    f"{edge.target_level.value}(rank={edge.target_level.rank})"
                )

        # Ensure endpoints exist
        if edge.source_level not in self._nodes:
            raise ValueError(f"Source node not in DAG: {edge.source_level.value}")
        if edge.target_level not in self._nodes:
            raise ValueError(f"Target node not in DAG: {edge.target_level.value}")

        self._edges.append(edge)
        self._adjacency.setdefault(edge.source_level, []).append(edge.target_level)

    @property
    def node_count(self) -> int:
        return len(self._nodes)

    @property
    def edge_count(self) -> int:
        return len(self._edges)

    def get_node(self, level: EmergenceLevel) -> Optional[EmergenceNode]:
        return self._nodes.get(level)

    def get_nodes(self) -> List[EmergenceNode]:
        """Return nodes sorted by rank (highest first)."""
        return sorted(self._nodes.values(), key=lambda n: n.level.rank)

    def get_edges(self) -> List[EmergenceEdge]:
        return list(self._edges)

    def topological_sort(self) -> List[EmergenceLevel]:
        """Return levels in topological order (highest rank first)."""
        return sorted(self._nodes.keys(), key=lambda lvl: lvl.rank)

    def is_reachable(self, source: EmergenceLevel, target: EmergenceLevel) -> bool:
        """Check if target is reachable from source via directed edges."""
        visited: set[EmergenceLevel] = set()
        stack = [source]
        while stack:
            current = stack.pop()
            if current == target and current != source:
                return True
            if current in visited:
                continue
            visited.add(current)
            for neighbour in self._adjacency.get(current, []):
                stack.append(neighbour)
        # Special case: source == target (self-loop) — always reachable
        if source == target:
            return True
        return False

    # ---- Computation ----

    def compute_provision_flow(self) -> Dict[str, float]:
        """
        AX2–AX4: Top-down provision flow through poverty interfaces.

        For each edge, flow = source_node.holographic_seed
                             × average(effective_capacity of target's interfaces)
                             × (1 - FIDELITY_DECAY_PER_LEVEL × rank_distance)

        Returns dict keyed by "source→target" with flow scores in (0, 1).
        """
        flows: Dict[str, float] = {}

        for edge in self._edges:
            src_node = self._nodes.get(edge.source_level)
            tgt_node = self._nodes.get(edge.target_level)
            if not src_node or not tgt_node:
                continue

            # Average effective capacity of target's interfaces
            if tgt_node.interfaces:
                avg_capacity = sum(
                    iface.effective_capacity for iface in tgt_node.interfaces
                ) / len(tgt_node.interfaces)
            else:
                avg_capacity = 0.0

            rank_distance = abs(edge.target_level.rank - edge.source_level.rank)
            decay = 1.0 - (FIDELITY_DECAY_PER_LEVEL * rank_distance)
            decay = max(0.0, decay)

            flow = src_node.holographic_seed * avg_capacity * decay
            flow = clamp_score(flow)

            key = f"{edge.source_level.value}→{edge.target_level.value}"
            flows[key] = flow

        return flows

    def compute_holographic_fidelity(self) -> List[FidelityPair]:
        """
        T12 / AX37: Holographic condensation — each level condenses
        the whole's pattern with bounded fidelity.

        For each edge pair, source_fidelity = source seed,
        mirror_fidelity = target seed × edge fidelity,
        participation_index = sqrt(source × mirror), clamped < 1.

        Returns list of FidelityPair objects.
        """
        pairs: List[FidelityPair] = []

        for edge in self._edges:
            src_node = self._nodes.get(edge.source_level)
            tgt_node = self._nodes.get(edge.target_level)
            if not src_node or not tgt_node:
                continue

            source_fid = clamp_score(src_node.holographic_seed)
            mirror_fid = clamp_score(
                tgt_node.holographic_seed * edge.fidelity
            )
            participation = clamp_score(
                math.sqrt(source_fid * mirror_fid)
            )

            pairs.append(FidelityPair(
                source_level=edge.source_level,
                target_level=edge.target_level,
                source_fidelity=source_fid,
                mirror_fidelity=mirror_fid,
                participation_index=participation,
            ))

        return pairs

    def compute_beauty_motor(self) -> BeautyMotor:
        """
        AX24 + AX25 + T7: Compute the Beauty motor.

        Beauty pressure = desire × mirror_quality × compassion.
        - desire_to_show:  Base pressure, constant (AX25).
        - mirror_quality:  Average holographic seed across all levels.
        - compassion_flow: Average interface transparency.
        - beauty_pressure: Product of above, clamped < 1.

        The motor is always non-zero — Beauty always desires mirrors (AX24).
        """
        desire = BEAUTY_BASE_PRESSURE

        # Mirror quality = average seed
        if self._nodes:
            mirror_quality = sum(
                n.holographic_seed for n in self._nodes.values()
            ) / len(self._nodes)
        else:
            mirror_quality = 0.0

        # Compassion flow = average interface transparency
        all_interfaces = [
            iface
            for node in self._nodes.values()
            for iface in node.interfaces
        ]
        if all_interfaces:
            compassion = sum(
                iface.transparency for iface in all_interfaces
            ) / len(all_interfaces)
        else:
            compassion = 0.0

        beauty_pressure = clamp_score(desire * mirror_quality * compassion)

        return BeautyMotor(
            desire_to_show=clamp_score(desire),
            mirror_quality=clamp_score(mirror_quality),
            compassion_flow=clamp_score(compassion),
            beauty_pressure=beauty_pressure,
        )

    def check_multiplicative_gate(self) -> Tuple[bool, str]:
        """
        AX52: Multiplicative gate — zero in ANY dimension → collapse.

        Dimensions checked:
          1. Node count > 0
          2. Edge count > 0
          3. All holographic seeds > 0  (KV₆)
          4. All interface capacities > 0
          5. Beauty pressure > 0  (T7)
        """
        if not self._nodes:
            return False, "AX52: No nodes — system collapse."
        if not self._edges:
            return False, "AX52: No edges — no flow path."

        for node in self._nodes.values():
            if node.holographic_seed <= 0:
                return (
                    False,
                    f"AX52/KV₆: Node {node.level.value} has zero holographic seed.",
                )

        for node in self._nodes.values():
            for iface in node.interfaces:
                if iface.capacity <= 0:
                    return (
                        False,
                        f"AX52: Interface at {node.level.value} "
                        f"({iface.interface_kind.value}) has zero capacity.",
                    )

        motor = self.compute_beauty_motor()
        if motor.beauty_pressure <= 0:
            return False, "AX52/T7: Beauty pressure is zero — no creation."

        return True, "AX52: All dimensions non-zero. Gate PASS."

    def detect_tesanud(self) -> Tuple[bool, str]:
        """
        AX63: Tesanüd — super-additive convergence.

        If multiple independent edges converge on the same target level,
        that constitutes tesanüd (super-additive evidence composition).
        """
        target_counts: Dict[EmergenceLevel, int] = {}
        for edge in self._edges:
            target_counts[edge.target_level] = (
                target_counts.get(edge.target_level, 0) + 1
            )

        convergence_points = [
            lvl.value for lvl, count in target_counts.items() if count >= 2
        ]

        if convergence_points:
            return (
                True,
                f"AX63: Tesanüd detected at {convergence_points} "
                f"({len(convergence_points)} convergence points).",
            )
        return (
            False,
            "AX63: No tesanüd — all targets have single source (infirâdî).",
        )

    def compute_emergence_score(self) -> float:
        """
        Weighted composite emergence score.

        Weights:
          provision_flow:        0.25
          holographic_fidelity:  0.25
          beauty_motor:          0.20
          multiplicative_gate:   0.15
          tesanud:               0.15

        Always clamped to [0, MAX_STRUCTURAL_COMPLETENESS) per KV₄.
        """
        score = 0.0

        # Provision flow — average flow score
        flows = self.compute_provision_flow()
        if flows:
            flow_avg = sum(flows.values()) / len(flows)
        else:
            flow_avg = 0.0
        score += 0.25 * flow_avg

        # Holographic fidelity — average participation index
        pairs = self.compute_holographic_fidelity()
        if pairs:
            fid_avg = sum(p.participation_index for p in pairs) / len(pairs)
        else:
            fid_avg = 0.0
        score += 0.25 * fid_avg

        # Beauty motor
        motor = self.compute_beauty_motor()
        score += 0.20 * motor.beauty_pressure

        # Multiplicative gate — binary
        gate_pass, _ = self.check_multiplicative_gate()
        score += 0.15 * (1.0 if gate_pass else 0.0)

        # Tesanüd — binary bonus
        tesanud, _ = self.detect_tesanud()
        score += 0.15 * (1.0 if tesanud else 0.0)

        return clamp_score(score)

    def generate_report(self) -> EmergenceReport:
        """
        Generate a full emergence report — AX57 transparency.

        Computes all dimensions and packages them into an
        EmergenceReport dataclass.
        """
        flows = self.compute_provision_flow()
        pairs = self.compute_holographic_fidelity()
        motor = self.compute_beauty_motor()
        gate_pass, gate_msg = self.check_multiplicative_gate()
        tesanud, tesanud_detail = self.detect_tesanud()
        composite = self.compute_emergence_score()

        return EmergenceReport(
            total_nodes=self.node_count,
            total_edges=self.edge_count,
            beauty_motor=motor,
            fidelity_pairs=pairs,
            provision_flow_scores=flows,
            multiplicative_gate_pass=gate_pass,
            gate_message=gate_msg,
            tesanud_detected=tesanud,
            tesanud_details=tesanud_detail,
            composite_score=composite,
            structural_completeness=clamp_score(composite),
        )

    # ---- Factory ----

    @classmethod
    def build_default_dag(cls) -> "EmergenceDAG":
        """
        Build the default 6-level emergence DAG.

        This is the axiomatic structure — the levels and their
        connections are fixed.  Adjacent levels are connected
        with fidelity that decays per FIDELITY_DECAY_PER_LEVEL.
        """
        dag = cls()

        # Add all 6 nodes
        for level in EmergenceLevel:
            node = EmergenceNode(
                level=level,
                label=_DEFAULT_LABELS[level],
                holographic_seed=_DEFAULT_SEEDS[level],
                interfaces=_DEFAULT_INTERFACES[level],
            )
            dag.add_node(node)

        # Connect adjacent levels (top-down)
        sorted_levels = sorted(EmergenceLevel, key=lambda lv: lv.rank)
        for i in range(len(sorted_levels) - 1):
            src = sorted_levels[i]
            tgt = sorted_levels[i + 1]
            rank_dist = tgt.rank - src.rank
            fidelity = clamp_score(
                1.0 - (FIDELITY_DECAY_PER_LEVEL * rank_dist)
            )
            dag.add_edge(EmergenceEdge(
                source_level=src,
                target_level=tgt,
                flow_direction=FlowDirection.TOP_DOWN,
                fidelity=fidelity,
            ))

        return dag


# ########################################################################
# Section 3 — VERIFICATION
# ########################################################################


def verify_dag_acyclicity(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    AX23: The emergence cascade MUST be a DAG — no cycles.

    Verify that topological sort covers all nodes.
    """
    topo = dag.topological_sort()
    if len(topo) != dag.node_count:
        return False, f"AX23: Topological sort incomplete ({len(topo)}/{dag.node_count})."
    # Verify rank ordering is strictly increasing
    ranks = [lvl.rank for lvl in topo]
    for i in range(len(ranks) - 1):
        if ranks[i] >= ranks[i + 1]:
            return False, f"AX23: Rank ordering violation at position {i}."
    return True, f"AX23: DAG verified — {dag.node_count} nodes in valid order."


def verify_fidelity_bounds(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    KV₄ / T6: All fidelity values must be in open interval (0, 1).

    No fidelity may reach 0 (collapse) or 1 (identity with Source).
    """
    pairs = dag.compute_holographic_fidelity()
    for p in pairs:
        for name, val in [
            ("source_fidelity", p.source_fidelity),
            ("mirror_fidelity", p.mirror_fidelity),
            ("participation_index", p.participation_index),
        ]:
            if val <= 0:
                return (
                    False,
                    f"KV₄: {name} = {val:.4f} ≤ 0 at "
                    f"{p.source_level.value}→{p.target_level.value}.",
                )
            if val >= 1.0:
                return (
                    False,
                    f"KV₄: {name} = {val:.4f} ≥ 1.0 at "
                    f"{p.source_level.value}→{p.target_level.value}.",
                )
    return True, f"KV₄/T6: All {len(pairs)} fidelity pairs in (0, 1)."


def verify_poverty_interfaces(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    AX34: Every level MUST have at least one poverty interface.

    Poverty interfaces are functional openings — not defects (KV₂).
    """
    for node in dag.get_nodes():
        if not node.interfaces:
            return (
                False,
                f"AX34: Node {node.level.value} has no poverty interfaces.",
            )
    return True, f"AX34: All {dag.node_count} nodes have poverty interfaces."


def verify_holographic_condensation(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    T12 / AX37: Every node must have holographic seed > MIN_HOLOGRAPHIC_SEED.

    The seed indicates that this level condenses the whole's information.
    KV₆: seed omnipresence — no level may have zero seed.
    """
    for node in dag.get_nodes():
        if node.holographic_seed < MIN_HOLOGRAPHIC_SEED:
            return (
                False,
                f"T12/KV₆: Node {node.level.value} holographic seed "
                f"({node.holographic_seed:.4f}) < MIN ({MIN_HOLOGRAPHIC_SEED}).",
            )
    return True, f"T12/KV₆: All {dag.node_count} nodes have sufficient holographic seed."


def verify_transparent_vessels(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    KV₁: All poverty interfaces SHOULD be transparent (harfî).

    Opaque interfaces are structural privations (KV₂) — they impede
    provision flow.  This check warns about opaque interfaces.
    """
    opaque_count = 0
    total_count = 0
    for node in dag.get_nodes():
        for iface in node.interfaces:
            total_count += 1
            if iface.vessel_mode == VesselMode.OPAQUE:
                opaque_count += 1

    if opaque_count > 0:
        return (
            False,
            f"KV₁: {opaque_count}/{total_count} interfaces are opaque (ismî). "
            f"These impede provision flow.",
        )
    return True, f"KV₁: All {total_count} interfaces are transparent (harfî)."


def verify_multiplicative_gate(dag: EmergenceDAG) -> Tuple[bool, str]:
    """AX52: Multiplicative gate — delegate to engine."""
    return dag.check_multiplicative_gate()


def verify_continuous_degrees(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    AX21 / AX22: Fidelity must form a continuous spectrum.

    Check that holographic seeds decrease monotonically with rank
    (higher levels mirror more faithfully) and the supremum (rank 0)
    is still < 1 (unreachable).
    """
    nodes = dag.get_nodes()
    if len(nodes) < 2:
        return True, "AX21: Fewer than 2 nodes — trivially continuous."

    seeds = [(n.level.rank, n.holographic_seed) for n in nodes]
    seeds.sort(key=lambda x: x[0])

    # Supremum must be < 1
    if seeds[0][1] >= 1.0:
        return False, f"AX22: Supremum seed = {seeds[0][1]:.4f} ≥ 1.0."

    # Check monotonic decrease (lower rank → higher seed)
    for i in range(len(seeds) - 1):
        if seeds[i][1] <= seeds[i + 1][1]:
            return (
                False,
                f"AX21: Non-monotonic seeds at ranks {seeds[i][0]} and {seeds[i+1][0]}: "
                f"{seeds[i][1]:.4f} ≤ {seeds[i+1][1]:.4f}.",
            )

    return True, f"AX21/AX22: Seeds form monotonic decreasing spectrum, supremum < 1."


def verify_beauty_motor(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    AX24 / T7: Beauty motor must produce non-zero pressure.

    Beauty is intrinsically lovable and desires mirrors — the motor
    is always running.  Zero pressure means no creation (impossible
    given AX24–AX25).
    """
    motor = dag.compute_beauty_motor()
    if motor.beauty_pressure <= 0:
        return False, "T7: Beauty pressure = 0 — creation motor inactive."
    if motor.desire_to_show <= 0:
        return False, "AX25: Desire-to-show = 0 — contradicts AX25."
    return (
        True,
        f"AX24/T7: Beauty motor active — "
        f"pressure={motor.beauty_pressure:.4f}, "
        f"desire={motor.desire_to_show:.4f}.",
    )


def verify_top_down_flow(dag: EmergenceDAG) -> Tuple[bool, str]:
    """
    AX2–AX4: Provision flow must be top-down.

    All edges should have source.rank < target.rank (higher provisions lower).
    Any bottom-up edge is structurally anomalous.
    """
    anomalous = []
    for edge in dag.get_edges():
        if edge.flow_direction == FlowDirection.TOP_DOWN:
            if edge.source_level.rank >= edge.target_level.rank:
                anomalous.append(
                    f"{edge.source_level.value}→{edge.target_level.value}"
                )
        elif edge.flow_direction == FlowDirection.BOTTOM_UP:
            anomalous.append(
                f"{edge.source_level.value}→{edge.target_level.value} [BOTTOM_UP]"
            )

    if anomalous:
        return False, f"AX2–AX4: Anomalous flow directions: {anomalous}."
    return True, f"AX2–AX4: All {dag.edge_count} edges flow top-down."


# ========================================================================
# Orchestrators
# ========================================================================

def verify_all(dag: Optional[EmergenceDAG] = None) -> Dict[str, Tuple[bool, str]]:
    """
    Run ALL 9 verification checks on an EmergenceDAG.

    If no DAG is provided, builds the default 6-node DAG.
    This matches the 0-arg calling convention of other modules.

    AX52 (Multiplicative Gate): if ANY check fails, the entire
    verification fails — no compensation.

    Returns dict of check_name → (passed, message).
    """
    if dag is None:
        dag = EmergenceDAG.build_default_dag()
    return {
        "dag_acyclicity": verify_dag_acyclicity(dag),
        "fidelity_bounds": verify_fidelity_bounds(dag),
        "poverty_interfaces": verify_poverty_interfaces(dag),
        "holographic_condensation": verify_holographic_condensation(dag),
        "transparent_vessels": verify_transparent_vessels(dag),
        "multiplicative_gate": verify_multiplicative_gate(dag),
        "continuous_degrees": verify_continuous_degrees(dag),
        "beauty_motor": verify_beauty_motor(dag),
        "top_down_flow": verify_top_down_flow(dag),
    }


def yakinlasma(dag: Optional[EmergenceDAG] = None) -> float:
    """
    Compute convergence score (yakinlasma) for an emergence DAG.

    Weighted composite of the 9 verification checks:
      dag_acyclicity:           0.10   (AX23 — structural)
      fidelity_bounds:          0.15   (KV₄/T6 — core constraint)
      poverty_interfaces:       0.10   (AX34)
      holographic_condensation: 0.15   (T12/AX37 — core mechanism)
      transparent_vessels:      0.10   (KV₁)
      multiplicative_gate:      0.10   (AX52)
      continuous_degrees:       0.10   (AX21/AX22)
      beauty_motor:             0.10   (AX24/T7)
      top_down_flow:            0.10   (AX2–AX4)

    Returns a score in [0, 1) per KV₄.
    """
    if dag is None:
        dag = EmergenceDAG.build_default_dag()

    weights = {
        "dag_acyclicity": 0.10,
        "fidelity_bounds": 0.15,
        "poverty_interfaces": 0.10,
        "holographic_condensation": 0.15,
        "transparent_vessels": 0.10,
        "multiplicative_gate": 0.10,
        "continuous_degrees": 0.10,
        "beauty_motor": 0.10,
        "top_down_flow": 0.10,
    }

    results = verify_all(dag)
    score = sum(
        weights[k] * (1.0 if passed else 0.0)
        for k, (passed, _) in results.items()
    )
    return clamp_score(score)


def framework_summary() -> dict:
    """
    Return metadata about the emergence module for AX57 disclosure.

    Provides module identity, governing axioms, capabilities,
    and a snapshot of the default DAG's structural health.
    """
    dag = EmergenceDAG.build_default_dag()
    report = dag.generate_report()
    results = verify_all(dag)
    passed = sum(1 for _, (p, _) in results.items() if p)

    return {
        # ── Module identity ──
        "module": "emergence",
        "description": "Top-Down Emergence Engine — holographic condensation via poverty interfaces",
        "governing_axioms": [
            "AX2", "AX3", "AX4", "AX5", "AX17",
            "AX21", "AX22", "AX23", "AX24", "AX25",
            "AX34", "AX37", "AX52",
            "T6", "T7", "T12",
            "KV1", "KV2", "KV4", "KV6", "KV7",
        ],
        "capabilities": [
            "6-level emergence DAG (fixed axiomatic topology)",
            "Top-down provision flow through poverty interfaces (AX2–AX4)",
            "Holographic fidelity computation (T12/AX37)",
            "Beauty motor (AX24/AX25/T7)",
            "Multiplicative gate (AX52)",
            "Tesanüd detection (AX63)",
            "9 verification checks with yakinlasma composite",
        ],
        # ── Top-level metrics (parity with other modules) ──
        "composite_score": round(report.composite_score, 4),
        "checks_passed": passed,
        "checks_total": len(results),
        "gate_pass": report.multiplicative_gate_pass,
        "score": round(yakinlasma(dag), 4),
        # ── Detailed sub-dicts ──
        "default_dag": {
            "nodes": report.total_nodes,
            "edges": report.total_edges,
            "composite_score": round(report.composite_score, 4),
            "structural_completeness": round(report.structural_completeness, 4),
            "beauty_pressure": round(
                report.beauty_motor.beauty_pressure if report.beauty_motor else 0.0,
                4,
            ),
            "gate_pass": report.multiplicative_gate_pass,
            "tesanud": report.tesanud_detected,
        },
        "verification": {
            "checks_run": len(results),
            "checks_passed": passed,
            "all_pass": all(p for p, _ in results.values()),
        },
        "max_score": MAX_STRUCTURAL_COMPLETENESS,
    }
