"""
kaskad/verification.py — Cascade Verification Layer.

Validates structural properties of CascadeDAGs against the
framework's formal constraints.

Governing axioms:
  AX23:  DAG property (acyclicity + transitivity)
  T8:    Neighborliness (detection prediction)
  KV₄:   Convergence bound (0 < C < 1)
  KV₆:   Holographic seed omnipresence (every chain > 0 nodes)
  KV₇:   Independence (chain isolation)
  AX63:  Tesanüd–İnfirâd asymmetry
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Set

from kaskad.engine import CascadeDAG, MAX_STRUCTURAL_COMPLETENESS
from kaskad.types import ChainId, ChainIntegrity


@dataclass(frozen=True)
class VerificationResult:
    """Result of a verification pass."""

    passed: bool
    check_name: str
    message: str
    details: Dict[str, object] = field(default_factory=dict)


def verify_acyclicity(dag: CascadeDAG) -> VerificationResult:
    """
    AX23: The cascade MUST be a DAG — no cycles permitted.

    This is redundant with the add_edge guard, but provides
    independent verification (KV₇ — independence of checks).
    """
    try:
        topo = dag.topological_sort()
        if len(topo) != dag.node_count:
            return VerificationResult(
                passed=False,
                check_name="AX23_acyclicity",
                message="Topological sort incomplete — possible cycle.",
                details={"sorted": len(topo), "total": dag.node_count},
            )
        return VerificationResult(
            passed=True,
            check_name="AX23_acyclicity",
            message=f"DAG verified: {dag.node_count} nodes in valid topological order.",
        )
    except RuntimeError as exc:
        return VerificationResult(
            passed=False,
            check_name="AX23_acyclicity",
            message=str(exc),
        )


def verify_transitivity_sample(
    dag: CascadeDAG,
    samples: int = 20,
) -> VerificationResult:
    """
    AX23: kaskad(a,b) ∧ kaskad(b,c) → kaskad(a,c)

    Sample edges and verify that if a→b and b→c exist as edges,
    then c is reachable from a (transitivity is structural, not
    requiring an explicit a→c edge — just reachability).
    """
    edges = dag.get_edges()
    checked = 0
    violations: List[str] = []

    for edge_ab in edges[:samples]:
        a, b = edge_ab.source, edge_ab.target
        for edge_bc in dag.get_edges():
            if edge_bc.source == b:
                c = edge_bc.target
                if not dag.is_reachable(a, c):
                    violations.append(f"{a}→{b}→{c}")
                checked += 1
                if checked >= samples:
                    break
        if checked >= samples:
            break

    return VerificationResult(
        passed=len(violations) == 0,
        check_name="AX23_transitivity",
        message=(
            f"Transitivity verified for {checked} pairs."
            if not violations
            else f"Transitivity violated: {violations[:5]}"
        ),
        details={"checked": checked, "violations": violations},
    )


def verify_convergence_bound(dag: CascadeDAG) -> VerificationResult:
    """
    KV₄: structural_completeness MUST be < 1.0.

    If ≥ 0.95, flag as warning (approaching danger zone).
    If ≥ 1.0, hard failure.
    """
    report = dag.generate_report()
    sc = report.structural_completeness

    if sc >= 1.0:
        return VerificationResult(
            passed=False,
            check_name="KV4_convergence_bound",
            message=f"CRITICAL: structural_completeness = {sc:.4f} ≥ 1.0. KV₄ violated.",
            details={"structural_completeness": sc},
        )
    if sc >= 0.95:
        return VerificationResult(
            passed=True,  # technically passes but with warning
            check_name="KV4_convergence_bound",
            message=f"WARNING: structural_completeness = {sc:.4f} ≥ 0.95. Near violation.",
            details={"structural_completeness": sc, "warning": True},
        )
    return VerificationResult(
        passed=True,
        check_name="KV4_convergence_bound",
        message=f"KV₄ satisfied: structural_completeness = {sc:.4f} < 1.0.",
        details={"structural_completeness": sc},
    )


def verify_chain_coverage(dag: CascadeDAG) -> VerificationResult:
    """
    KV₆: Holographic seed omnipresence — every chain should
    have at least 1 node. Empty chains indicate structural gaps.
    """
    empty_chains: List[str] = []
    chain_sizes: Dict[str, int] = {}

    for chain in ChainId:
        nodes = dag.nodes_in_chain(chain)
        chain_sizes[chain.value] = len(nodes)
        if len(nodes) == 0:
            empty_chains.append(chain.value)

    return VerificationResult(
        passed=len(empty_chains) == 0,
        check_name="KV6_chain_coverage",
        message=(
            f"All chains populated: {chain_sizes}"
            if not empty_chains
            else f"Empty chains (structural gap): {empty_chains}"
        ),
        details={"chain_sizes": chain_sizes, "empty": empty_chains},
    )


def verify_chain_integrity(dag: CascadeDAG) -> VerificationResult:
    """
    Per-chain structural health: acyclicity + connectivity.
    """
    failures: List[str] = []
    integrity_map: Dict[str, Dict[str, object]] = {}

    for chain in ChainId:
        ci = dag.analyze_chain(chain)
        if ci.node_count == 0:
            continue

        integrity_map[chain.value] = {
            "acyclic": ci.is_acyclic,
            "connected": ci.is_connected,
            "nodes": ci.node_count,
            "edges": ci.edge_count,
            "depth": ci.max_depth,
        }

        if not ci.is_acyclic:
            failures.append(f"{chain.value}: not acyclic")
        if not ci.is_connected:
            failures.append(f"{chain.value}: not connected")

    return VerificationResult(
        passed=len(failures) == 0,
        check_name="chain_integrity",
        message=(
            f"All {len(integrity_map)} chains structurally sound."
            if not failures
            else f"Chain failures: {failures}"
        ),
        details={"chains": integrity_map, "failures": failures},
    )


def verify_bridge_nodes(dag: CascadeDAG) -> VerificationResult:
    """
    §IG.3: Cross-chain bridges must exist for tesanüd.

    Per KV₇, independent chains reaching the same node
    constitute super-additive convergence (AX63).
    At minimum, KV₄ should appear in ≥2 chains.
    """
    bridges = dag.find_bridges()
    tesanud = [t for t in dag.analyze_tesanud() if t.is_tesanud]

    return VerificationResult(
        passed=len(bridges) > 0,
        check_name="IG3_bridges",
        message=(
            f"{len(bridges)} bridge nodes, {len(tesanud)} tesanüd convergence points."
            if bridges
            else "No bridge nodes — chains are fully isolated (infirâdî only)."
        ),
        details={
            "bridge_count": len(bridges),
            "bridges": bridges,
            "tesanud_count": len(tesanud),
        },
    )


def verify_sources_and_sinks(dag: CascadeDAG) -> VerificationResult:
    """
    Every DAG must have source nodes (axioms) and sink nodes (output rules).

    Sources = in-degree 0 (foundational axioms, şuûnât)
    Sinks   = out-degree 0 (output rules, terminals)
    """
    sources = dag.source_nodes()
    sinks = dag.sink_nodes()

    return VerificationResult(
        passed=len(sources) > 0 and len(sinks) > 0,
        check_name="source_sink_check",
        message=f"{len(sources)} sources, {len(sinks)} sinks.",
        details={
            "source_count": len(sources),
            "sink_count": len(sinks),
            "sources": sorted(sources),
            "sinks": sorted(sinks),
        },
    )


# ========================================================================
# Main verification orchestrator
# ========================================================================

def verify_all(dag: CascadeDAG) -> List[VerificationResult]:
    """
    Run ALL verification checks on a CascadeDAG.

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

    Returns list of VerificationResults (all checks run regardless).
    """
    return [
        verify_acyclicity(dag),
        verify_transitivity_sample(dag),
        verify_convergence_bound(dag),
        verify_chain_coverage(dag),
        verify_chain_integrity(dag),
        verify_bridge_nodes(dag),
        verify_sources_and_sinks(dag),
    ]


def is_fully_verified(results: List[VerificationResult]) -> bool:
    """
    AX52: Multiplicative gate — ALL must pass.
    Zero in any dimension = system-level failure.
    """
    return all(r.passed for r in results)


# ========================================================================
# Framework summary (AX57 — transparency disclosure)
# ========================================================================

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

    Provides module identity, governing axioms, capabilities,
    and a live snapshot of the framework graph's structural health.
    Called by fw_server's get_framework_summary() tool.
    """
    from kaskad.chains import build_framework_graph

    dag = build_framework_graph()
    report = dag.generate_report()
    diag = dag.self_diagnostic()

    checks = verify_all(dag)
    passed = sum(1 for c in checks if c.passed)

    return {
        "module": "kaskad",
        "description": "Generative Cascade Engine (AX23)",
        "governing_axioms": ["AX23", "T7", "T8", "AX63", "KV4", "KV7", "AX57"],
        "capabilities": [
            "Typed DAG with 6 edge morphisms (§IG.1)",
            "T8 Neighborliness — predict detectable nodes from observed",
            "AX63 Tesanüd — super-additive convergence across chains",
            "§IG.3 Hub/bridge analysis — cross-chain connection points",
            "Self-diagnostic — engine validates its OWN integrity",
            "7-chain framework graph (C1–C7) with 56 nodes, 61 edges",
            "Full verification layer (7 structural checks)",
        ],
        "framework_graph": {
            "nodes": report.total_nodes,
            "edges": report.total_edges,
            "chains": report.chain_count,
            "max_depth": report.max_depth,
            "structural_completeness": round(report.structural_completeness, 4),
            "bridge_nodes": report.bridge_nodes,
            "tesanud_count": sum(1 for t in report.tesanud_nodes if t.is_tesanud),
        },
        "self_diagnostic": diag,
        "verification": {
            "checks_run": len(checks),
            "checks_passed": passed,
            "all_pass": is_fully_verified(checks),
        },
        "max_score": 0.9999,
    }
