"""RunReceipt: Contract-aware execution receipt for agent tasks and system upgrades.

A RunReceipt captures the full context and outcome of a task executed under a
TaskContract, including:

  - What contract governed the run (contract_id, severity)
  - How it was scored (spec_compliance, global_quality, raw_score)
  - What gates were applied (variance_gate_events, gate_reason)
  - Whether constraints were satisfied (token_over_budget)
  - Routing decisions made (which models, agents)
  - Complete score history for replay

This extends the base receipt infrastructure to make contract-based task execution
fully auditable and replayable.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, List, Literal, Optional

from receipts.base import BaseReceipt, Domain
from receipts.compat.pyd import BaseModel, ConfigDictLike, Field


class RunReceipt(BaseReceipt):
    """Receipt documenting execution of a task under a TaskContract.

    This receipt captures:
      - The contract that governed the run
      - Scoring (both spec and global quality)
      - Variance gate decisions
      - Constraint violations (e.g., token over budget)
      - Model/agent routing decisions
      - Complete execution history for replay
    """

    domain: str = Field(default=Domain.GOVERNANCE.value)
    receipt_type: str = "RunReceipt"

    # Contract context
    contract_id: Optional[str] = Field(
        default=None,
        description="Identifier of the TaskContract that governed this run.",
    )

    task_type: str = Field(
        default="unknown",
        description="Task type from the contract (e.g., 'code_generation', 'system_upgrade')",
    )

    severity: Literal["toy", "internal", "production_candidate", "production"] = Field(
        default="internal",
        description="Severity tier of the task (governs variance gating).",
    )

    # Scoring (dual-score model)
    spec_compliance_score: Optional[float] = Field(
        default=None,
        ge=0.0,
        le=1.0,
        description="How well output matched explicit contract/spec (0–1).",
    )

    global_quality_score: Optional[float] = Field(
        default=None,
        ge=0.0,
        le=1.0,
        description="Overall quality by general standards (0–1).",
    )

    final_critic_score: Optional[int] = Field(
        default=None,
        ge=0,
        le=10,
        description="Final critic score (0–10) after variance gating.",
    )

    # Constraint & gate tracking
    token_over_budget: bool = Field(
        default=False,
        description="True if output exceeded max_tokens_out constraint.",
    )

    safety_flags: List[str] = Field(
        default_factory=list,
        description="Safety concerns detected: 'missing_safety_check', 'contradicts_prior', etc.",
    )

    gate_reason: Optional[str] = Field(
        default=None,
        description="Reason score was gated/clamped: 'variance_gate_applied', 'ceiling_exceeded', etc.",
    )

    # Score history for replay & diagnostics
    score_history: List[int] = Field(
        default_factory=list,
        description="All critic scores assigned during iterative refinement (oldest to newest).",
    )

    # Routing & execution context
    routing_decision: Dict[str, Any] = Field(
        default_factory=dict,
        description="Which models/agents were routed: {'primary_model': '...', 'critic_model': '...'}",
    )

    variance_gate_events: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="Log of variance gate decisions: [{step: 2, proposed: 7, gated: 6, reason: '...'}]",
    )

    # Metadata
    token_budget: Optional[int] = Field(
        default=None,
        description="Max tokens allowed by contract (if any).",
    )

    tokens_used: Optional[int] = Field(
        default=None,
        description="Actual tokens consumed in generation.",
    )

    iteration_count: int = Field(
        default=0,
        description="Number of refinement iterations performed.",
    )

    total_prompt_tokens: int = Field(
        default=0,
        description="Total prompt tokens (context) sent to model.",
    )

    total_completion_tokens: int = Field(
        default=0,
        description="Total completion tokens generated.",
    )

    model_config = ConfigDictLike(
        json_schema_extra={
            "examples": [
                {
                    "receipt_id": "run_coherence_gate_v2_2025-11-21T06:00:00Z",
                    "contract_id": "coherence_gate_v2_upgrade_v0.1",
                    "severity": "production_candidate",
                    "spec_compliance_score": 0.92,
                    "global_quality_score": 0.78,
                    "final_critic_score": 7,
                    "token_budget": 2200,
                    "tokens_used": 1847,
                    "score_history": [5, 6, 6, 7, 7],
                    "iteration_count": 5,
                    "gate_reason": "variance_gate_applied",
                    "safety_flags": [],
                    "routing_decision": {
                        "architect_model": "claude-sonnet-4.5",
                        "critic_model": "claude-haiku-4.5",
                    },
                }
            ]
        }
    )

    @property
    def passed_spec(self, min_spec: float = 0.8) -> bool:
        """True if spec_compliance meets the contract threshold."""
        if self.spec_compliance_score is None:
            return False
        return self.spec_compliance_score >= min_spec

    @property
    def passed_global(self, min_global: float = 0.6) -> bool:
        """True if global_quality meets the contract threshold."""
        if self.global_quality_score is None:
            return False
        return self.global_quality_score >= min_global

    @property
    def is_safe(self) -> bool:
        """True if no critical safety flags are present."""
        critical = {"token_over_budget", "missing_safety_check"}
        return not any(f in self.safety_flags for f in critical)

    def passed_contract(self, min_spec: float = 0.8, min_global: float = 0.6) -> bool:
        """True if run meets both spec and global quality thresholds."""
        return self.passed_spec(min_spec) and self.passed_global(min_global)
