# receipts/glimpse.py
"""
Glimpse Protocol receipt schemas (JCS-stable, frozen).
Tracks contradiction→insight→repair lineage.
"""
from typing import List, Dict, Any, Optional, Union
from pydantic import BaseModel, Field, ConfigDict
from datetime import datetime


class GlimpseTrigger(BaseModel):
    """Trigger condition that initiated a Glimpse sprint."""
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    kind: str = Field(..., description="contradiction | incompleteness | novelty")
    confidence: float = Field(..., ge=0.0, le=1.0)
    source_a_id: Optional[str] = None
    source_b_id: Optional[str] = None
    claim_a: Optional[str] = None
    claim_b: Optional[str] = None


class SocraticPhase(BaseModel):
    """Socratic questioning (S phase) outputs."""
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    questions: List[str] = Field(..., min_length=3, max_length=5,
                                  description="Identity, Boundary, Symmetry, Conservation, Counterfactual")
    analogy: Optional[str] = None


class NewtonianPhase(BaseModel):
    """Newtonian draft (N phase) outputs."""
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    toy_law: str = Field(..., description="Candidate explanatory rule")
    observables: List[str] = Field(default_factory=list)
    one_shot_test: Optional[str] = None


class DarwinianPhase(BaseModel):
    """Darwinian selection (D phase) outputs."""
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    mutants_generated: int
    champion_id: str
    scores: Dict[str, float] = Field(default_factory=dict,
                                      description="compression_gain_bits, grip_delta_brier, falsifiability_ok")


class CRIFEnvelope(BaseModel):
    """CRIF governance metadata for the Glimpse run."""
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    time_budget_ms: int
    used_ms: int
    cycles: int
    max_cycles: int
    flow_ok: bool
    clause0_screened: bool
    dignity_floor_maintained: bool = Field(default=True)


class GlimpseSeedReceipt(BaseModel):
    """
    Minted when a contradiction/uncertainty/novelty triggers a Glimpse.
    Acts as the parent receipt for the subsequent GlimpseReceipt.
    """
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    receipt_type: str = Field(default="GlimpseSeedReceipt")
    receipt_id: str
    timestamp: str  # ISO8601 string for JCS stability
    domain: str
    trigger: GlimpseTrigger
    parent_ids: List[str] = Field(default_factory=list,
                                   description="RefusalStone, ProphecyStone, or OutcomeStone IDs that triggered this")
    witness_hash: Optional[str] = None


class GlimpseReceipt(BaseModel):
    """
    Records a complete S→N→D Glimpse sprint.
    JCS-stable hash serves as verifiable proof of the insight generation process.
    """
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    receipt_type: str = Field(default="GlimpseReceipt")
    receipt_id: str
    timestamp: str  # ISO8601 string for JCS stability
    domain: str
    
    seed_id: str
    socratic: SocraticPhase
    newtonian: NewtonianPhase
    darwinian: DarwinianPhase
    crif: CRIFEnvelope
    
    disposition: str = Field(..., description="converted_to_insight | composted | refused")
    insight_excerpt: Optional[str] = None
    
    parent_ids: List[str] = Field(default_factory=list)
    witness_hash: Optional[str] = None


class ProofOfRepairReceipt(BaseModel):
    """
    Minted when a Glimpse-generated insight is validated and promoted.
    Links the insight to measurable improvement (ΔBrier, ΔAccuracy, etc.).
    """
    model_config = ConfigDict(frozen=True, protected_namespaces=())
    
    receipt_type: str = Field(default="ProofOfRepairReceipt")
    receipt_id: str
    timestamp: str  # ISO8601 string for JCS stability
    domain: str
    
    seed_id: str
    glimpse_id: str
    
    applies_to: List[str] = Field(..., description="Rules, cohorts, or gates this repair affects")
    
    before_after: Dict[str, float] = Field(...,
                                            description="brier_before, brier_after, ece_before, ece_after, n_eval")
    
    promotion: Dict[str, Any] = Field(default_factory=dict,
                                       description="gatebook_rule_added, commit_hash, pr_url, auto_applied")
    
    parent_ids: List[str] = Field(default_factory=list)
    witness_hash: Optional[str] = None


# Example instances (for docs/tests)
EXAMPLE_GLIMPSE_SEED = {
    "receipt_type": "GlimpseSeedReceipt",
    "receipt_id": "GLS_2025-11-02_001",
    "timestamp": "2025-11-02T19:00:00Z",
    "domain": "ops",
    "trigger": {
        "kind": "contradiction",
        "confidence": 0.81,
        "source_a_id": "ProphecyStone_abc123",
        "source_b_id": "OutcomeStone_def456",
        "claim_a": "Model predicted p=0.9 for approval",
        "claim_b": "Human override denied (p_actual=0.0)"
    },
    "parent_ids": ["ProphecyStone_abc123", "OutcomeStone_def456"],
    "witness_hash": "sha256:deadbeef..."
}

EXAMPLE_GLIMPSE = {
    "receipt_type": "GlimpseReceipt",
    "receipt_id": "GLM_2025-11-02_001",
    "timestamp": "2025-11-02T19:00:30Z",
    "domain": "ops",
    "seed_id": "GLS_2025-11-02_001",
    "socratic": {
        "questions": [
            "Identity: What is 'approval confidence'?",
            "Boundary: When does high confidence fail?",
            "Symmetry: Do denials show same pattern?",
            "Conservation: Is total calibration error conserved?",
            "Counterfactual: If we had flagged uncertainty?"
        ],
        "analogy": "Like weather forecasting: high confidence for clear days, but storms are edge cases"
    },
    "newtonian": {
        "toy_law": "ΔOverride_rate = f(edge_distance, p_prior_divergence)",
        "observables": ["edge_distance", "p_prior_divergence", "override_rate"],
        "one_shot_test": "Measure override_rate for cases with edge_distance < 0.1 vs > 0.3"
    },
    "darwinian": {
        "mutants_generated": 5,
        "champion_id": "m3",
        "scores": {
            "compression_gain_bits": 8.2,
            "grip_delta_brier": -0.012,
            "falsifiability_ok": True
        }
    },
    "crif": {
        "time_budget_ms": 30000,
        "used_ms": 18420,
        "cycles": 1,
        "max_cycles": 2,
        "flow_ok": True,
        "clause0_screened": True,
        "dignity_floor_maintained": True
    },
    "disposition": "converted_to_insight",
    "insight_excerpt": "Overrides spike when model confidence is high but p_prior divergence > 0.15",
    "parent_ids": ["GLS_2025-11-02_001"],
    "witness_hash": "sha256:cafebabe..."
}

EXAMPLE_PROOF_OF_REPAIR = {
    "receipt_type": "ProofOfRepairReceipt",
    "receipt_id": "POR_2025-11-02_007",
    "timestamp": "2025-11-02T19:01:10Z",
    "domain": "ops",
    "seed_id": "GLS_2025-11-02_001",
    "glimpse_id": "GLM_2025-11-02_001",
    "applies_to": ["gate:prior_divergence_threshold", "cohorts:pa_triage"],
    "before_after": {
        "brier_before": 0.214,
        "brier_after": 0.202,
        "ece_before": 0.031,
        "ece_after": 0.027,
        "n_eval": 480
    },
    "promotion": {
        "gatebook_rule_added": True,
        "commit_hash": "abc123def",
        "pr_url": "https://github.com/Haserjian/Loom/pull/42",
        "auto_applied": False
    },
    "parent_ids": ["GLM_2025-11-02_001"],
    "witness_hash": "sha256:f00dface..."
}
