"""
Clinical receipt schemas for Trial-and-Learning Medicine (TLM).

Extends Loom's base receipt system with healthcare-specific prophecies, outcomes,
and refusals. Supports sepsis prediction, readmission risk, and other clinical
decision support use cases.
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import Enum
from typing import Any, Dict, List, Optional, Tuple

from pydantic import Field

from receipts.base import BaseReceipt, GateStatus
from receipts.compat.pyd import BaseModel, ConfigDictLike
from receipts.domains.mlb import ProphecyStone, RefusalStone, OutcomeReceipt
from tools.proofkit import ProofKit

# Initialize ProofKit for clinical receipts
proofkit = ProofKit()


class ClinicalOutcomeType(str, Enum):
    """Types of clinical outcomes that can be predicted."""

    SEPSIS = "sepsis"
    READMISSION_30D = "readmission_30d"
    MORTALITY_INHOSPITAL = "mortality_inhospital"
    MORTALITY_30D = "mortality_30d"
    ICU_TRANSFER = "icu_transfer"
    MECHANICAL_VENTILATION = "mechanical_ventilation"
    AKI = "acute_kidney_injury"
    DELIRIUM = "delirium"
    DVT = "deep_vein_thrombosis"
    BLEEDING_MAJOR = "major_bleeding"


class ResolutionType(str, Enum):
    """How an outcome label was resolved."""

    CONFIRMED = "confirmed"
    NEGATED = "negated"
    CENSORED = "censored"
    INDETERMINATE = "indeterminate"


class ClinicalDataQualityIssue(str, Enum):
    """Categories of data quality problems in clinical settings."""

    VITALS_STALE = "vitals_stale"  # Measurements too old
    LABS_MISSING = "labs_missing"  # Critical labs not available
    SENSOR_DRIFT = "sensor_drift"  # Equipment calibration suspect
    CONTRADICTORY = "contradictory_readings"  # Inconsistent data
    OUT_OF_RANGE = "physiologically_impossible"  # Values outside plausible bounds
    INCOMPLETE_HISTORY = "incomplete_patient_history"
    NO_BASELINE = "no_baseline_for_comparison"


class ClinicalRefusalReason(str, Enum):
    """Reasons why clinical Guardian refuses to make prediction."""

    # Data quality
    DATA_QUALITY_LOW = "data_quality_below_threshold"
    DATA_STALE = "data_too_stale"
    DATA_MISSING = "critical_data_missing"
    DATA_INCOMPLETE = "critical_data_incomplete"
    DATA_CONTRADICTORY = "contradictory_data"

    # Safety
    DIGNITY_FLOOR_VIOLATED = "patient_dignity_floor_violated"
    CONSENT_MISSING = "valid_consent_not_on_file"
    CONSENT_NOT_OBTAINED = "consent_not_obtained"
    OUT_OF_DISTRIBUTION = "patient_profile_too_novel"

    # Calibration
    CALIBRATION_POOR = "calibration_below_threshold"
    UNCERTAINTY_HIGH = "confidence_interval_too_wide"

    # Regulatory
    REGULATORY_CONSTRAINT = "regulatory_constraint_violated"
    PROTECTED_CLASS_BIAS = "potential_bias_detected"


class ClinicalProphecyStone(BaseReceipt):
    """
    Healthcare-specific prediction receipt.

    Extends ProphecyStone with clinical metadata, dignity scoring,
    data quality tracking, and transparency requirements for medical AI.
    """

    receipt_type: str = "ClinicalProphecyStone"

    # Profile and receipt mode for healthcare compliance
    profile: str = Field(default="healthcare", description="Profile determining signing behavior")
    receipt_mode: str = Field(default="signed", description="Receipt mode: 'signed' or 'minimal'")
    retention_days: Optional[int] = Field(default=None, description="Optional retention period in days")
    encryption_required: bool = Field(default=True, description="Whether encryption is required for PHI")

    # Core prediction (from ProphecyStone)
    pred_prob: float = Field(
        description="Predicted probability of outcome (0.0-1.0)"
    )
    ci90_lo: float = Field(
        description="Lower bound of 90% confidence interval"
    )
    ci90_hi: float = Field(
        description="Upper bound of 90% confidence interval"
    )
    ci90_method: Optional[str] = Field(
        default="bootstrap",
        description="Method used to compute confidence interval"
    )

    # Clinical specifics
    patient_id_hash: str = Field(
        description="SHA256(patient_id + hospital_salt) for privacy"
    )
    outcome_type: ClinicalOutcomeType = Field(
        description="Type of clinical outcome being predicted"
    )
    prediction_horizon: str = Field(
        description="Time window for prediction (e.g., '24h', '30d')"
    )
    prediction_horizon_seconds: float = Field(
        description="Prediction horizon in seconds (for machine readability)"
    )

    # Transparency & explainability
    features_used: List[str] = Field(
        default_factory=list,
        description="Features that influenced prediction (e.g., ['heart_rate', 'lactate', 'age'])"
    )
    feature_importance: Dict[str, float] = Field(
        default_factory=dict,
        description="Normalized feature importance scores (sum to 1.0)"
    )
    similar_cases: List[str] = Field(
        default_factory=list,
        description="Hashes of similar historical cases (k-nearest neighbors)"
    )
    similar_cases_count: int = Field(
        default=0,
        description="Number of similar cases found in memory"
    )

    # Clinical dignity & safety
    dignity_index: float = Field(
        description="Patient dignity score at prediction time (0.0-1.0)"
    )
    dignity_components: Dict[str, float] = Field(
        default_factory=dict,
        description="Breakdown of dignity score: physiological, psychological, autonomy, fairness"
    )
    data_quality_score: float = Field(
        description="Overall data quality score (0.0-1.0)"
    )
    data_quality_issues: List[ClinicalDataQualityIssue] = Field(
        default_factory=list,
        description="Detected data quality problems"
    )

    # Guardian gate results
    guardian_checks: Dict[str, GateStatus] = Field(
        default_factory=dict,
        description="Results of Guardian gate checks"
    )
    gates_passed: bool = Field(
        description="Whether all Guardian gates passed"
    )

    # Clinical context
    patient_demographics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Redacted demographics (age_group, sex, race_ethnicity if relevant)"
    )
    clinical_context: Dict[str, Any] = Field(
        default_factory=dict,
        description="Relevant clinical context (admission_type, location, comorbidities)"
    )

    # Temporal tracking
    data_freshness_seconds: Dict[str, float] = Field(
        default_factory=dict,
        description="Age of each data source in seconds (vitals, labs, etc.)"
    )
    oldest_data_age_seconds: float = Field(
        default=0.0,
        description="Age of oldest critical data point used"
    )

    # Regulatory & compliance
    hipaa_safe: bool = Field(
        default=True,
        description="Receipt contains no PHI per HIPAA Safe Harbor"
    )
    consent_obtained: bool = Field(
        description="Valid patient consent on file"
    )
    irb_protocol_id: Optional[str] = Field(
        default=None,
        description="IRB protocol number if research use"
    )

    # Proof Rails vNext attestation
    attestation: Dict[str, Any] = Field(
        default_factory=dict,
        description="Cryptographic attestation block for integrity"
    )

    def to_clinician_summary(self) -> str:
        """
        Generate human-readable summary for clinicians.

        Returns:
            Formatted text explanation of prediction
        """
        outcome_name = self.outcome_type.value.replace("_", " ").title()
        risk_pct = self.pred_prob * 100
        ci_lo_pct = self.ci90_lo * 100
        ci_hi_pct = self.ci90_hi * 100

        summary = f"""
Clinical Prediction Summary
{'=' * 50}
Outcome: {outcome_name}
Timeframe: next {self.prediction_horizon}
Risk: {risk_pct:.1f}% (90% CI: {ci_lo_pct:.1f}%-{ci_hi_pct:.1f}%)

Key Factors:
{self._format_top_features(n=5)}

Data Quality: {self.data_quality_score:.2f}/1.00
Dignity Score: {self.dignity_index:.2f}/1.00
Gates Passed: {'✓ Yes' if self.gates_passed else '✗ No'}

Similar Cases: {self.similar_cases_count} historical patients
Oldest Data: {self._format_age(self.oldest_data_age_seconds)}

Receipt ID: {self.receipt_id}
Timestamp: {self.created_at.isoformat() if hasattr(self, 'created_at') else 'N/A'}
{'=' * 50}
"""
        return summary.strip()

    def _format_top_features(self, n: int = 5) -> str:
        """Format top N features by importance."""
        if not self.feature_importance:
            return "  (No feature importance available)"

        sorted_features = sorted(
            self.feature_importance.items(),
            key=lambda x: x[1],
            reverse=True
        )[:n]

        lines = []
        for feature, importance in sorted_features:
            bar_length = int(importance * 20)
            bar = "█" * bar_length
            lines.append(f"  {feature:20s} {bar} {importance:.1%}")

        return "\n".join(lines)

    def _format_age(self, seconds: float) -> str:
        """Format age in human-readable units."""
        if seconds < 60:
            return f"{seconds:.0f}s"
        elif seconds < 3600:
            return f"{seconds/60:.0f}min"
        else:
            return f"{seconds/3600:.1f}hr"

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Ensure new fields are present explicitly
        data["profile"] = self.profile
        data["receipt_mode"] = self.receipt_mode
        data["retention_days"] = self.retention_days
        data["encryption_required"] = self.encryption_required
        return data

    def sign(self, key: bytes, algorithm: str = "hmac-sha256") -> None:
        """
        Sign this clinical prophecy with Proof Rails vNext attestation.

        Args:
            key: Signing key
            algorithm: Signing algorithm ("hmac-sha256" or "ed25519")
        """
        if self.receipt_mode != "signed":
            import logging
            logger = logging.getLogger(__name__)
            logger.debug(f"Skipping signature for ClinicalProphecyStone {self.receipt_id} (receipt_mode: {self.receipt_mode})")
            return

        try:
            # Create signed receipt data
            signed_receipt = proofkit.sign_receipt(
                self.to_dict(),
                key,
                algorithm
            )
            # Extract attestation block
            self.attestation = signed_receipt.get("attestation", {})
        except Exception as e:
            # Log error but don't fail - clinical systems must be robust
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Failed to sign ClinicalProphecyStone {self.receipt_id}: {e}")

    def verify_signature(self, key: bytes, algorithm: str = "hmac-sha256") -> bool:
        """
        Verify the cryptographic signature of this clinical prophecy.

        Args:
            key: Verification key
            algorithm: Algorithm used for signing

        Returns:
            True if signature is valid
        """
        if not self.attestation:
            return False

        try:
            receipt_dict = self.model_dump()
            return proofkit.verify_receipt_signature(
                receipt_dict,
                key,
                algorithm
            )
        except Exception:
            return False


class ClinicalOutcome(OutcomeReceipt):
    """
    Links clinical prediction to actual outcome.

    Extends OutcomeReceipt with clinical resolution types and
    adjudication metadata.
    """

    receipt_type: str = "ClinicalOutcome"

    # Profile and receipt mode for healthcare compliance
    profile: str = Field(default="healthcare", description="Profile determining signing behavior")
    receipt_mode: str = Field(default="signed", description="Receipt mode: 'signed' or 'minimal'")
    retention_days: Optional[int] = Field(default=None, description="Optional retention period in days")
    encryption_required: bool = Field(default=True, description="Whether encryption is required for PHI")

    # Override parent fields with clinical specifics
    patient_id_hash: str = Field(
        description="SHA256(patient_id + hospital_salt)"
    )
    prophecy_rid: str = Field(
        description="Receipt ID of linked ClinicalProphecyStone"
    )

    # Outcome details
    outcome_occurred: bool = Field(
        description="Whether predicted outcome actually occurred"
    )
    outcome_time: Optional[datetime] = Field(
        default=None,
        description="Timestamp when outcome occurred (if it did)"
    )
    time_to_outcome_seconds: Optional[float] = Field(
        default=None,
        description="Seconds between prediction and outcome"
    )

    # Clinical resolution
    resolution_type: ResolutionType = Field(
        description="How outcome was resolved: confirmed, negated, censored, indeterminate"
    )
    resolution_confidence: float = Field(
        default=1.0,
        description="Confidence in outcome label (0.0-1.0)"
    )

    # Clinical adjudication
    adjudicated_by: Optional[str] = Field(
        default=None,
        description="Clinician ID who confirmed outcome (if manual review)"
    )
    adjudication_notes: Optional[str] = Field(
        default=None,
        description="Redacted clinical notes about outcome determination"
    )
    icd10_codes: List[str] = Field(
        default_factory=list,
        description="ICD-10 diagnosis codes associated with outcome"
    )

    # Outcome context
    outcome_severity: Optional[str] = Field(
        default=None,
        description="Severity if outcome occurred: mild, moderate, severe, critical"
    )
    interventions_performed: List[str] = Field(
        default_factory=list,
        description="Interventions taken (e.g., ['antibiotics', 'fluid_resuscitation'])"
    )

    # Regulatory
    hipaa_safe: bool = Field(
        default=True,
        description="Outcome receipt contains no PHI"
    )

    # Proof Rails vNext attestation
    attestation: Dict[str, Any] = Field(
        default_factory=dict,
        description="Cryptographic attestation block for integrity"
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Ensure new fields are present explicitly
        data["profile"] = self.profile
        data["receipt_mode"] = self.receipt_mode
        data["retention_days"] = self.retention_days
        data["encryption_required"] = self.encryption_required
        return data


class ClinicalRefusalStone(RefusalStone):
    """
    Clinical-specific refusal receipt.

    Extends RefusalStone with healthcare-specific refusal reasons,
    clinical context, and actionable recommendations.
    """

    receipt_type: str = "ClinicalRefusalStone"

    # Profile and receipt mode for healthcare compliance
    profile: str = Field(default="healthcare", description="Profile determining signing behavior")
    receipt_mode: str = Field(default="signed", description="Receipt mode: 'signed' or 'minimal'")
    retention_days: Optional[int] = Field(default=None, description="Optional retention period in days")
    encryption_required: bool = Field(default=True, description="Whether encryption is required for PHI")

    # Override reasons with clinical enum
    refusal_reason_code: ClinicalRefusalReason = Field(
        description="Primary reason for refusal"
    )
    refusal_reasons: List[ClinicalRefusalReason] = Field(
        default_factory=list,
        description="All reasons contributing to refusal"
    )

    # Clinical context
    patient_id_hash: str = Field(
        description="SHA256(patient_id + hospital_salt)"
    )
    clinical_context: str = Field(
        description="Brief clinical explanation (e.g., 'vitals_stale_6h')"
    )

    # Data quality specifics
    data_quality_score: float = Field(
        description="Overall data quality score (led to refusal)"
    )
    data_quality_issues: List[ClinicalDataQualityIssue] = Field(
        default_factory=list,
        description="Specific data quality problems detected"
    )
    data_freshness_seconds: Dict[str, float] = Field(
        default_factory=dict,
        description="Age of each data source"
    )
    missing_critical_data: List[str] = Field(
        default_factory=list,
        description="Critical data elements that were missing"
    )

    # Dignity & safety
    dignity_index: Optional[float] = Field(
        default=None,
        description="Patient dignity score if computed"
    )
    dignity_violations: List[str] = Field(
        default_factory=list,
        description="Specific dignity facets that failed"
    )

    # Guardian checks
    guardian_checks: Dict[str, GateStatus] = Field(
        default_factory=dict,
        description="Results of Guardian gate checks"
    )
    failed_gates: List[str] = Field(
        default_factory=list,
        description="Names of gates that failed"
    )

    # Actionable recommendations
    recommendation: str = Field(
        description="What clinician should do (e.g., 'Order fresh lactate test')"
    )
    required_actions: List[str] = Field(
        default_factory=list,
        description="Specific actions to enable prediction"
    )
    estimated_delay_minutes: Optional[float] = Field(
        default=None,
        description="Estimated time to obtain required data"
    )

    # Regulatory
    hipaa_safe: bool = Field(
        default=True,
        description="Refusal receipt contains no PHI"
    )
    consent_obtained: bool = Field(
        default=False,
        description="Whether consent was on file"
    )

    # Proof Rails vNext attestation
    attestation: Dict[str, Any] = Field(
        default_factory=dict,
        description="Cryptographic attestation block for integrity"
    )

    def to_clinician_summary(self) -> str:
        """Generate human-readable refusal explanation for clinicians."""
        summary = f"""
Prediction Refused - Action Required
{'=' * 50}
Reason: {self.refusal_reason_code.value.replace('_', ' ').title()}
Context: {self.clinical_context}

Data Quality: {self.data_quality_score:.2f}/1.00
Failed Gates: {', '.join(self.failed_gates) if self.failed_gates else 'N/A'}

Recommendation:
  {self.recommendation}

Required Actions:
{self._format_required_actions()}

Estimated Time: {self.estimated_delay_minutes or 'Unknown'} minutes

Receipt ID: {self.receipt_id}
{'=' * 50}
"""
        return summary.strip()

    def _format_required_actions(self) -> str:
        """Format required actions as bullet list."""
        if not self.required_actions:
            return "  (None specified)"
        return "\n".join(f"  • {action}" for action in self.required_actions)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Ensure new fields are present explicitly
        data["profile"] = self.profile
        data["receipt_mode"] = self.receipt_mode
        data["retention_days"] = self.retention_days
        data["encryption_required"] = self.encryption_required
        return data

    def sign(self, key: bytes, algorithm: str = "hmac-sha256") -> None:
        """
        Sign this clinical refusal with Proof Rails vNext attestation.
        """
        if self.receipt_mode != "signed":
            import logging
            logger = logging.getLogger(__name__)
            logger.debug(f"Skipping signature for ClinicalRefusalStone {self.receipt_id} (receipt_mode: {self.receipt_mode})")
            return

        try:
            signed_receipt = proofkit.sign_receipt(self.to_dict(), key, algorithm)
            self.attestation = signed_receipt.get("attestation", {})
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Failed to sign ClinicalRefusalStone {self.receipt_id}: {e}")

    def verify_signature(self, key: bytes, algorithm: str = "hmac-sha256") -> bool:
        """
        Verify the cryptographic signature of this clinical refusal.
        """
        if not self.attestation:
            return False
        try:
            receipt_dict = self.model_dump()
            return proofkit.verify_receipt_signature(receipt_dict, key, algorithm)
        except Exception:
            return False

    def sign(self, key: bytes, algorithm: str = "hmac-sha256") -> None:
        """
        Sign this clinical outcome with Proof Rails vNext attestation.
        """
        if self.receipt_mode != "signed":
            import logging
            logger = logging.getLogger(__name__)
            logger.debug(f"Skipping signature for ClinicalOutcome {self.receipt_id} (receipt_mode: {self.receipt_mode})")
            return

        try:
            signed_receipt = proofkit.sign_receipt(self.to_dict(), key, algorithm)
            self.attestation = signed_receipt.get("attestation", {})
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Failed to sign ClinicalOutcome {self.receipt_id}: {e}")

    def verify_signature(self, key: bytes, algorithm: str = "hmac-sha256") -> bool:
        """
        Verify the cryptographic signature of this clinical outcome.
        """
        if not self.attestation:
            return False
        try:
            return proofkit.verify_receipt_signature(self.to_dict(), key, algorithm)
        except Exception:
            return False


__all__ = [
    "ClinicalOutcomeType",
    "ClinicalDataQualityIssue",
    "ClinicalRefusalReason",
    "ClinicalProphecyStone",
    "ClinicalOutcome",
    "ClinicalRefusalStone",
]
