"""
PolicyExperimentReceipt: Receipt domain for Quintet policy experiments.

Every policy experiment and recommendation is recorded as a receipt,
making the policy evolution loop auditable and constitutional.

Schema: policy_experiment.v1
"""
from __future__ import annotations

import hashlib
import json
import logging
from datetime import datetime, timezone
from typing import Any, Dict, List, Literal, Optional

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

logger = logging.getLogger(__name__)


class ExperimentMetrics(BaseModel):
    """Metrics from a policy experiment analysis."""

    control_success: float = Field(ge=0.0, le=1.0, description="Success rate in control group")
    treatment_success: float = Field(ge=0.0, le=1.0, description="Success rate in treatment group")
    control_n: int = Field(ge=0, description="Number of episodes in control group")
    treatment_n: int = Field(ge=0, description="Number of episodes in treatment group")
    improvement: float = Field(description="Improvement (treatment - control)")
    confidence: float = Field(ge=0.0, le=1.0, description="Confidence in recommendation")

    # Optional extended metrics
    control_latency_ms: Optional[float] = Field(default=None, description="Avg latency in control")
    treatment_latency_ms: Optional[float] = Field(default=None, description="Avg latency in treatment")
    control_cost_cents: Optional[float] = Field(default=None, description="Avg cost in control")
    treatment_cost_cents: Optional[float] = Field(default=None, description="Avg cost in treatment")


class PolicyExperimentReceipt(BaseReceipt):
    """
    Receipt emitted when Quintet analyzes episodes and produces a recommendation.

    This makes policy evolution auditable:
    - Every analysis run is recorded
    - Evidence is hashed for integrity
    - Recommendations are traceable to specific experiments
    - Rollbacks can reference the original experiment

    Example:
        {
            "schema": "policy_experiment.v1",
            "experiment_id": "exp_2025-12-10T02:34:00Z",
            "lever": "brain_temperature",
            "action": "PROMOTE",
            "from_value": 0.9,
            "to_value": 0.7,
            "evidence_hash": "sha256:abc123...",
            "episodes_analyzed": 219,
            "metrics": { ... },
            "initiator": "operator:tim"
        }
    """

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

    # Experiment identification
    experiment_id: str = Field(description="Unique experiment identifier (e.g., exp_2025-12-10T02:34:00Z)")
    lever: str = Field(description="Policy lever being analyzed (e.g., brain_temperature, provider_slot)")

    # Recommendation
    action: Literal["PROMOTE", "HOLD", "ROLLBACK"] = Field(
        description="Quintet's recommendation"
    )
    from_value: Any = Field(description="Current value of the lever")
    to_value: Any = Field(description="Recommended new value (same as from_value for HOLD)")

    # Evidence
    evidence_hash: str = Field(
        description="SHA-256 hash of the full evidence JSON for integrity verification"
    )
    episodes_analyzed: int = Field(ge=0, description="Number of episodes in the analysis")
    analysis_window_start: Optional[datetime] = Field(
        default=None, description="Start of episode window analyzed"
    )
    analysis_window_end: Optional[datetime] = Field(
        default=None, description="End of episode window analyzed"
    )

    # Metrics summary
    metrics: ExperimentMetrics = Field(description="Key metrics from the experiment")

    # Stratification info
    strata_analyzed: List[str] = Field(
        default_factory=list,
        description="List of strata keys analyzed (e.g., ['council:ops', 'brain:sandbox'])"
    )

    # Provenance
    initiator: str = Field(
        default="agent:quintet",
        description="Who initiated this experiment (operator:name or agent:quintet)"
    )
    quintet_version: Optional[str] = Field(
        default=None, description="Quintet version that produced this recommendation"
    )

    # Guardrails
    delta_capped: bool = Field(
        default=False, description="Whether the recommendation was clamped by guardrails"
    )
    original_to_value: Optional[Any] = Field(
        default=None, description="Original recommended value before clamping"
    )

    # Stress test gate (Move 3 integration point)
    stress_test_passed: Optional[bool] = Field(
        default=None, description="Whether pre-promotion stress tests passed"
    )
    stress_test_failures: List[str] = Field(
        default_factory=list, description="Names of failed stress scenarios"
    )

    # Application status (filled in when/if applied)
    applied: bool = Field(default=False, description="Whether this recommendation was applied")
    applied_at: Optional[datetime] = Field(default=None, description="When it was applied")
    applied_by: Optional[str] = Field(default=None, description="Who applied it")
    rollback_of: Optional[str] = Field(
        default=None, description="If this is a rollback, ID of the original experiment"
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = self.model_dump(mode="json")
        # Ensure metrics is serialized
        if hasattr(self.metrics, "model_dump"):
            data["metrics"] = self.metrics.model_dump()
        elif hasattr(self.metrics, "dict"):
            data["metrics"] = self.metrics.dict()
        return data


def compute_evidence_hash(evidence: Dict[str, Any]) -> str:
    """
    Compute SHA-256 hash of evidence JSON for integrity verification.

    The evidence is canonicalized (sorted keys, no whitespace) before hashing.
    """
    canonical = json.dumps(evidence, sort_keys=True, separators=(",", ":"), default=str)
    digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
    return f"sha256:{digest}"


def create_experiment_receipt(
    experiment_id: str,
    lever: str,
    action: Literal["PROMOTE", "HOLD", "ROLLBACK"],
    from_value: Any,
    to_value: Any,
    evidence: Dict[str, Any],
    episodes_analyzed: int,
    metrics: Dict[str, Any],
    strata: List[str],
    initiator: str = "agent:quintet",
    delta_capped: bool = False,
    original_to_value: Optional[Any] = None,
    stress_test_passed: Optional[bool] = None,
    stress_test_failures: Optional[List[str]] = None,
) -> PolicyExperimentReceipt:
    """
    Create a PolicyExperimentReceipt from analysis results.

    This is the primary factory function for creating experiment receipts.
    """
    import uuid

    receipt_id = f"pexp_{uuid.uuid4().hex[:16]}"
    evidence_hash = compute_evidence_hash(evidence)

    experiment_metrics = ExperimentMetrics(
        control_success=metrics.get("control_success", 0.0),
        treatment_success=metrics.get("treatment_success", 0.0),
        control_n=metrics.get("control_n", 0),
        treatment_n=metrics.get("treatment_n", 0),
        improvement=metrics.get("improvement", 0.0),
        confidence=metrics.get("confidence", 0.0),
        control_latency_ms=metrics.get("control_latency_ms"),
        treatment_latency_ms=metrics.get("treatment_latency_ms"),
        control_cost_cents=metrics.get("control_cost_cents"),
        treatment_cost_cents=metrics.get("treatment_cost_cents"),
    )

    return PolicyExperimentReceipt(
        receipt_id=receipt_id,
        experiment_id=experiment_id,
        lever=lever,
        action=action,
        from_value=from_value,
        to_value=to_value,
        evidence_hash=evidence_hash,
        episodes_analyzed=episodes_analyzed,
        metrics=experiment_metrics,
        strata_analyzed=strata,
        initiator=initiator,
        delta_capped=delta_capped,
        original_to_value=original_to_value,
        stress_test_passed=stress_test_passed,
        stress_test_failures=stress_test_failures or [],
    )
