from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, List, Literal, Mapping, Optional

import math

from receipts.compat.pyd import BaseModel, ConfigDictLike, Field
from receipts.base import BaseReceipt
from receipts.domains.mlb import ProphecyStone


class CRIFPhase(str, Enum):
    """Enumerates the canonical CRIF metabolic phases."""

    FLOW = "flow"
    CONTRADICTION = "contradiction"
    RECURSION = "recursion"
    INCOMPLETENESS = "incompleteness"


def _clamp(x: float, lo: float, hi: float) -> float:
    return max(lo, min(hi, x))


def compute_contradiction_score(pred_prob: Optional[float], market_prior: Optional[float]) -> float:
    """
    Simple contradiction score between model probability and market prior.

    Uses absolute difference when both are available; otherwise 0.0.
    """
    try:
        if pred_prob is None or market_prior is None:
            return 0.0
        return abs(float(pred_prob) - float(market_prior))
    except Exception:
        return 0.0


def compute_incompleteness_gap(
    *,
    dignity_dwf: Optional[float] = None,
    ece_overall: Optional[float] = None,
    ece_worst: Optional[float] = None,
) -> float:
    """
    A conservative measure of incompleteness (the creative aperture).

    - Prefer 1 - DWF when available (claims remaining uncertainty preserved)
    - Otherwise fall back to worst-slice ECE, then overall ECE
    - Clamp to [1e-6, 0.99] for numerical stability
    """
    candidates: List[float] = []
    if dignity_dwf is not None:
        try:
            candidates.append(1.0 - float(dignity_dwf))
        except Exception:
            pass
    for v in (ece_worst, ece_overall):
        if v is None:
            continue
        try:
            candidates.append(float(v))
        except Exception:
            continue
    gap = max(candidates) if candidates else 0.1
    return _clamp(gap, 1e-6, 0.99)


def classify_flow_regime(flow_now: Optional[float], *, default: str = "laminar") -> Literal["laminar", "turbulent", "frozen"]:
    """Heuristic classification of flow regime from a scalar flow score [0,1]."""
    if flow_now is None:
        return "frozen"
    try:
        v = _clamp(float(flow_now), 0.0, 1.0)
    except Exception:
        return default  # pragma: no cover - defensive fallback
    if v >= 0.66:
        return "laminar"
    if v >= 0.33:
        return "turbulent"
    return "frozen"


def compute_paradox_pressure(contradiction: float, recursion_depth: int, incompleteness_gap: float) -> float:
    """
    P_p = C × log(1+R) / (1 - I)
    Numerically stable with clamping.
    """
    C = max(0.0, float(contradiction))
    R = max(0, int(recursion_depth))
    I = _clamp(float(incompleteness_gap), 1e-6, 0.99)
    return C * math.log1p(R) / (1.0 - I)


def compute_reality_coefficient(
    *,
    contradiction: float,
    recursion: int,
    incompleteness: float,
    flow: float,
) -> float:
    """
    RealityCoeff ≈ (C × log(1+R) × I) ^ Φ, with clamps for stability.
    """
    C = max(0.0, float(contradiction))
    R = max(0, int(recursion))
    I = _clamp(float(incompleteness), 1e-6, 0.99)
    F = _clamp(float(flow), 0.0, 1.0)
    base = C * math.log1p(R) * I
    # Avoid 0 ** F surprises
    base = max(base, 1e-9)
    return float(base ** (max(F, 1e-9)))


class CRIFStone(BaseReceipt):
    """
    Metabolic receipt that captures CRIF state derived from a source decision.

    Fields are computed from a source receipt (typically ProphecyStone).
    """

    receipt_type: str = "CRIFStone"
    source_receipt_id: str = Field(description="Origin receipt ID (e.g., ProphecyStone)")
    source_type: str = Field(description="Origin receipt type")

    # Core CRIF features
    contradiction_score: float = Field(description="|p_model - p_market|")
    recursion_depth: int = Field(description="Chain depth inferred from parent_ids length")
    incompleteness_gap: float = Field(description="Conservative aperture: max(1-DWF, ECE_worst, ECE)")
    flow_regime: Literal["laminar", "turbulent", "frozen"] = Field(
        default="laminar", description="Heuristic flow regime classification"
    )

    # Derived scalars
    paradox_pressure: float = Field(description="C × log(1+R) / (1 - I)")
    reality_coefficient: float = Field(description="(C × log(1+R) × I)^Φ")

    notes: Optional[str] = None


class MetabolicPhaseReceipt(BaseReceipt):
    """Marks a CRIF phase transition for auditable metabolics."""

    receipt_type: str = "MetabolicPhaseReceipt"
    from_phase: Literal["contradiction", "recursion", "incompleteness", "flow"]
    to_phase: Literal["contradiction", "recursion", "incompleteness", "flow"]
    catalyst: str = Field(description="Trigger or condition causing the transition")
    related_receipt_id: Optional[str] = Field(
        default=None, description="Optional linked decision receipt ID"
    )


def crif_from_prophecy(prophecy: ProphecyStone) -> CRIFStone:
    """Build a CRIFStone from a ProphecyStone instance."""
    # Contradiction
    C = compute_contradiction_score(prophecy.pred_prob, prophecy.market_prior)

    # Recursion (approximate from parent chain length)
    R = len(prophecy.parent_ids or [])

    # Incompleteness (prefer typed metrics when present)
    dwf = None
    if getattr(prophecy, "dignity_typed", None) is not None:
        try:
            dwf = prophecy.dignity_typed.dwf  # type: ignore[attr-defined]
        except Exception:
            dwf = None
    ece_overall = None
    if getattr(prophecy, "calibration_typed", None) is not None:
        try:
            ece_overall = prophecy.calibration_typed.ece  # type: ignore[attr-defined]
        except Exception:
            ece_overall = None

    metrics = prophecy.metrics or {}
    ece_worst = None
    for k in ("ece_worst", "worst_slice_ece", "ece_max"):
        v = metrics.get(k)
        if v is not None:
            try:
                ece_worst = float(v)
            except Exception:
                pass
            break
    I = compute_incompleteness_gap(dignity_dwf=dwf, ece_overall=ece_overall, ece_worst=ece_worst)

    # Flow
    flow_now = getattr(prophecy, "flow_now", None)
    flow_regime = classify_flow_regime(flow_now)
    flow_scalar = 0.66 if flow_now is None else _clamp(float(flow_now), 0.0, 1.0)

    # Derived scalars
    Pp = compute_paradox_pressure(C, R, I)
    Rc = compute_reality_coefficient(contradiction=C, recursion=R, incompleteness=I, flow=flow_scalar)

    return CRIFStone(
        receipt_id=f"CRIF_{prophecy.receipt_id}",
        domain=prophecy.domain,
        source_receipt_id=prophecy.receipt_id,
        source_type=prophecy.type,
        contradiction_score=C,
        recursion_depth=R,
        incompleteness_gap=I,
        flow_regime=flow_regime,
        paradox_pressure=Pp,
        reality_coefficient=Rc,
    )


__all__ = [
    "CRIFPhase",
    "CRIFStone",
    "MetabolicPhaseReceipt",
    "compute_contradiction_score",
    "compute_incompleteness_gap",
    "classify_flow_regime",
    "compute_paradox_pressure",
    "compute_reality_coefficient",
    "crif_from_prophecy",
]
