"""
Context Health Receipts: Audit trail for session health monitoring.

These receipts track context health metrics to detect and prevent
long-context safety instabilities where models may become erratic,
skip safety checks, or exhibit degraded judgment in extended sessions.

Receipt types:
- ContextHealthCheckReceipt: Periodic health snapshot
- ContextHealthVerdictReceipt: Gate decision based on health metrics
- SessionResetReceipt: When session reset occurs (forced or voluntary)

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

import uuid
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional

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

import logging

logger = logging.getLogger(__name__)


# ============================================================================
# Enums
# ============================================================================

class ContextHealthStatus(str, Enum):
    """Overall health status of the context/session."""
    HEALTHY = "HEALTHY"
    DEGRADED = "DEGRADED"
    CRITICAL = "CRITICAL"
    FORCE_RESET = "FORCE_RESET"


class ContextHealthDecision(str, Enum):
    """Decision from Context Health Gate."""
    ALLOW = "ALLOW"
    ALLOW_WITH_WARNING = "ALLOW_WITH_WARNING"
    REQUIRE_CONFIRMATION = "REQUIRE_CONFIRMATION"
    DENY = "DENY"
    FORCE_RESET = "FORCE_RESET"


class ResetReason(str, Enum):
    """Reason for session reset."""
    USER_REQUESTED = "user_requested"
    CONTEXT_CRITICAL = "context_critical"
    TOKEN_LIMIT = "token_limit"
    ERROR_RATE_EXCEEDED = "error_rate_exceeded"
    HIGH_RISK_LIMIT = "high_risk_limit"
    SESSION_TIMEOUT = "session_timeout"
    OPERATOR_FORCED = "operator_forced"


class HealthMetricStatus(str, Enum):
    """Status of individual health metric."""
    OK = "ok"
    WARNING = "warning"
    CRITICAL = "critical"


# ============================================================================
# Health Metrics Model
# ============================================================================

class ContextHealthMetrics(BaseModel):
    """
    Current health metrics for a session/context.

    These metrics are tracked continuously and checked before
    each action to detect context degradation.
    """

    # Token usage
    context_tokens_used: int = Field(
        default=0,
        ge=0,
        description="Current token count in the context"
    )
    context_tokens_limit: Optional[int] = Field(
        default=None,
        description="Maximum token limit for the model"
    )
    token_utilization: float = Field(
        default=0.0,
        ge=0.0,
        le=1.0,
        description="Ratio of tokens used to limit"
    )

    # Conversation metrics
    turns_count: int = Field(
        default=0,
        ge=0,
        description="Number of conversation turns"
    )
    messages_count: int = Field(
        default=0,
        ge=0,
        description="Total messages in conversation"
    )

    # Risk tracking
    high_risk_action_count: int = Field(
        default=0,
        ge=0,
        description="Cumulative HIGH/CRITICAL risk actions this session"
    )
    actions_since_reset: int = Field(
        default=0,
        ge=0,
        description="Total actions since last reset"
    )

    # Time tracking
    session_start: Optional[datetime] = Field(
        default=None,
        description="When the session started"
    )
    session_duration_minutes: float = Field(
        default=0.0,
        ge=0.0,
        description="How long session has been active"
    )

    # Error tracking (windowed)
    recent_error_count: int = Field(
        default=0,
        ge=0,
        description="Errors in the recent window"
    )
    error_window_minutes: int = Field(
        default=10,
        description="Window size for error tracking"
    )
    error_rate: float = Field(
        default=0.0,
        ge=0.0,
        description="Error rate in current window (errors per action)"
    )

    # Individual metric statuses
    token_status: HealthMetricStatus = Field(
        default=HealthMetricStatus.OK,
        description="Status of token utilization"
    )
    turns_status: HealthMetricStatus = Field(
        default=HealthMetricStatus.OK,
        description="Status of turn count"
    )
    high_risk_status: HealthMetricStatus = Field(
        default=HealthMetricStatus.OK,
        description="Status of high-risk action count"
    )
    duration_status: HealthMetricStatus = Field(
        default=HealthMetricStatus.OK,
        description="Status of session duration"
    )
    error_status: HealthMetricStatus = Field(
        default=HealthMetricStatus.OK,
        description="Status of error rate"
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dict for serialization."""
        return {
            "context_tokens_used": self.context_tokens_used,
            "context_tokens_limit": self.context_tokens_limit,
            "token_utilization": self.token_utilization,
            "turns_count": self.turns_count,
            "messages_count": self.messages_count,
            "high_risk_action_count": self.high_risk_action_count,
            "actions_since_reset": self.actions_since_reset,
            "session_start": self.session_start.isoformat() if self.session_start else None,
            "session_duration_minutes": self.session_duration_minutes,
            "recent_error_count": self.recent_error_count,
            "error_window_minutes": self.error_window_minutes,
            "error_rate": self.error_rate,
            "token_status": self.token_status.value,
            "turns_status": self.turns_status.value,
            "high_risk_status": self.high_risk_status.value,
            "duration_status": self.duration_status.value,
            "error_status": self.error_status.value,
        }


# ============================================================================
# Health Thresholds Configuration
# ============================================================================

class ContextHealthThresholds(BaseModel):
    """
    Configurable thresholds for context health checks.

    Each metric has WARNING and CRITICAL thresholds.
    """

    # Token thresholds
    token_warning: int = Field(
        default=50_000,
        description="Token count warning threshold"
    )
    token_critical: int = Field(
        default=100_000,
        description="Token count critical threshold"
    )

    # Turn thresholds
    turns_warning: int = Field(
        default=50,
        description="Turn count warning threshold"
    )
    turns_critical: int = Field(
        default=100,
        description="Turn count critical threshold"
    )

    # High-risk action thresholds
    high_risk_warning: int = Field(
        default=10,
        description="High-risk action count warning threshold"
    )
    high_risk_critical: int = Field(
        default=25,
        description="High-risk action count critical threshold"
    )

    # Duration thresholds (minutes)
    duration_warning_minutes: int = Field(
        default=60,
        description="Session duration warning threshold (minutes)"
    )
    duration_critical_minutes: int = Field(
        default=180,
        description="Session duration critical threshold (minutes)"
    )

    # Error rate thresholds
    error_rate_warning: float = Field(
        default=0.1,
        description="Error rate warning threshold (10%)"
    )
    error_rate_critical: float = Field(
        default=0.25,
        description="Error rate critical threshold (25%)"
    )

    # Force reset threshold (any metric at this multiple of critical triggers reset)
    force_reset_multiplier: float = Field(
        default=1.5,
        description="Multiplier of critical threshold that triggers force reset"
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dict for serialization."""
        return {
            "token_warning": self.token_warning,
            "token_critical": self.token_critical,
            "turns_warning": self.turns_warning,
            "turns_critical": self.turns_critical,
            "high_risk_warning": self.high_risk_warning,
            "high_risk_critical": self.high_risk_critical,
            "duration_warning_minutes": self.duration_warning_minutes,
            "duration_critical_minutes": self.duration_critical_minutes,
            "error_rate_warning": self.error_rate_warning,
            "error_rate_critical": self.error_rate_critical,
            "force_reset_multiplier": self.force_reset_multiplier,
        }


# ============================================================================
# Receipt: Context Health Check
# ============================================================================

class ContextHealthCheckReceipt(BaseReceipt):
    """
    Receipt emitted for periodic context health snapshots.

    These are emitted at configurable intervals to provide
    a continuous audit trail of session health.

    Schema: context_health.v1
    """

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

    # Session identity
    session_id: str = Field(
        description="Session being monitored"
    )
    check_number: int = Field(
        default=1,
        ge=1,
        description="Sequential check number in this session"
    )

    # Health snapshot
    status: ContextHealthStatus = Field(
        description="Overall health status"
    )
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Full metrics snapshot"
    )

    # Thresholds in effect
    thresholds: Dict[str, Any] = Field(
        default_factory=dict,
        description="Thresholds used for this check"
    )

    # Warnings raised
    warnings: List[str] = Field(
        default_factory=list,
        description="Warning messages for metrics approaching thresholds"
    )

    # Critical issues
    critical_issues: List[str] = Field(
        default_factory=list,
        description="Critical issues detected"
    )

    @classmethod
    def create(
        cls,
        session_id: str,
        status: ContextHealthStatus,
        metrics: ContextHealthMetrics,
        thresholds: ContextHealthThresholds,
        check_number: int = 1,
        warnings: Optional[List[str]] = None,
        critical_issues: Optional[List[str]] = None,
    ) -> "ContextHealthCheckReceipt":
        """Create a new health check receipt."""
        now = datetime.now(timezone.utc)

        return cls(
            receipt_id=f"rcpt_chc_{session_id}_{now.strftime('%Y%m%dT%H%M%S')}_{uuid.uuid4().hex[:8]}",
            ts=now,
            session_id=session_id,
            check_number=check_number,
            status=status,
            metrics=metrics.to_dict(),
            thresholds=thresholds.to_dict(),
            warnings=warnings or [],
            critical_issues=critical_issues or [],
        )


# ============================================================================
# Receipt: Context Health Verdict
# ============================================================================

class ContextHealthVerdictReceipt(BaseReceipt):
    """
    Receipt emitted by Context Health Gate with its decision.

    This is emitted before each action to record whether the
    action should proceed given current context health.

    Schema: context_health.v1
    """

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

    # Session and action identity
    session_id: str = Field(
        description="Session being checked"
    )
    action_id: str = Field(
        description="ID of the action being gated"
    )
    action_type: Optional[str] = Field(
        default=None,
        description="Type of action (tool name, etc.)"
    )
    action_risk_level: Optional[str] = Field(
        default=None,
        description="Risk level of the action (LOW/MEDIUM/HIGH/CRITICAL)"
    )

    # Health at decision time
    health_status: ContextHealthStatus = Field(
        description="Health status when decision was made"
    )
    metrics_snapshot: Dict[str, Any] = Field(
        default_factory=dict,
        description="Metrics at decision time"
    )

    # Decision
    decision: ContextHealthDecision = Field(
        description="Gate decision"
    )
    decision_reason: str = Field(
        default="",
        description="Human-readable reason for decision"
    )

    # Blocking details (if denied)
    blocking_metrics: List[str] = Field(
        default_factory=list,
        description="Which metrics triggered the block"
    )

    # Confirmation required (if REQUIRE_CONFIRMATION)
    confirmation_required: bool = Field(
        default=False,
        description="Whether user confirmation is required"
    )
    confirmation_message: Optional[str] = Field(
        default=None,
        description="Message to show user for confirmation"
    )

    # Timing
    latency_ms: float = Field(
        default=0.0,
        description="Gate evaluation time in milliseconds"
    )

    @classmethod
    def create_allow(
        cls,
        session_id: str,
        action_id: str,
        health_status: ContextHealthStatus,
        metrics: ContextHealthMetrics,
        action_type: Optional[str] = None,
        action_risk_level: Optional[str] = None,
        latency_ms: float = 0.0,
    ) -> "ContextHealthVerdictReceipt":
        """Create an ALLOW verdict."""
        return cls(
            receipt_id=f"rcpt_chv_{action_id}",
            ts=datetime.now(timezone.utc),
            session_id=session_id,
            action_id=action_id,
            action_type=action_type,
            action_risk_level=action_risk_level,
            health_status=health_status,
            metrics_snapshot=metrics.to_dict(),
            decision=ContextHealthDecision.ALLOW,
            decision_reason="Context health within acceptable limits",
            latency_ms=latency_ms,
        )

    @classmethod
    def create_allow_with_warning(
        cls,
        session_id: str,
        action_id: str,
        health_status: ContextHealthStatus,
        metrics: ContextHealthMetrics,
        warnings: List[str],
        action_type: Optional[str] = None,
        action_risk_level: Optional[str] = None,
        latency_ms: float = 0.0,
    ) -> "ContextHealthVerdictReceipt":
        """Create an ALLOW_WITH_WARNING verdict."""
        return cls(
            receipt_id=f"rcpt_chv_{action_id}",
            ts=datetime.now(timezone.utc),
            session_id=session_id,
            action_id=action_id,
            action_type=action_type,
            action_risk_level=action_risk_level,
            health_status=health_status,
            metrics_snapshot=metrics.to_dict(),
            decision=ContextHealthDecision.ALLOW_WITH_WARNING,
            decision_reason=f"Warnings: {'; '.join(warnings)}",
            blocking_metrics=warnings,
            latency_ms=latency_ms,
        )

    @classmethod
    def create_require_confirmation(
        cls,
        session_id: str,
        action_id: str,
        health_status: ContextHealthStatus,
        metrics: ContextHealthMetrics,
        blocking_metrics: List[str],
        confirmation_message: str,
        action_type: Optional[str] = None,
        action_risk_level: Optional[str] = None,
        latency_ms: float = 0.0,
    ) -> "ContextHealthVerdictReceipt":
        """Create a REQUIRE_CONFIRMATION verdict."""
        return cls(
            receipt_id=f"rcpt_chv_{action_id}",
            ts=datetime.now(timezone.utc),
            session_id=session_id,
            action_id=action_id,
            action_type=action_type,
            action_risk_level=action_risk_level,
            health_status=health_status,
            metrics_snapshot=metrics.to_dict(),
            decision=ContextHealthDecision.REQUIRE_CONFIRMATION,
            decision_reason="Context degraded - confirmation required for risky actions",
            blocking_metrics=blocking_metrics,
            confirmation_required=True,
            confirmation_message=confirmation_message,
            latency_ms=latency_ms,
        )

    @classmethod
    def create_deny(
        cls,
        session_id: str,
        action_id: str,
        health_status: ContextHealthStatus,
        metrics: ContextHealthMetrics,
        blocking_metrics: List[str],
        reason: str,
        action_type: Optional[str] = None,
        action_risk_level: Optional[str] = None,
        latency_ms: float = 0.0,
    ) -> "ContextHealthVerdictReceipt":
        """Create a DENY verdict."""
        return cls(
            receipt_id=f"rcpt_chv_{action_id}",
            ts=datetime.now(timezone.utc),
            session_id=session_id,
            action_id=action_id,
            action_type=action_type,
            action_risk_level=action_risk_level,
            health_status=health_status,
            metrics_snapshot=metrics.to_dict(),
            decision=ContextHealthDecision.DENY,
            decision_reason=reason,
            blocking_metrics=blocking_metrics,
            latency_ms=latency_ms,
        )

    @classmethod
    def create_force_reset(
        cls,
        session_id: str,
        action_id: str,
        metrics: ContextHealthMetrics,
        blocking_metrics: List[str],
        reason: str,
        action_type: Optional[str] = None,
        latency_ms: float = 0.0,
    ) -> "ContextHealthVerdictReceipt":
        """Create a FORCE_RESET verdict."""
        return cls(
            receipt_id=f"rcpt_chv_{action_id}",
            ts=datetime.now(timezone.utc),
            session_id=session_id,
            action_id=action_id,
            action_type=action_type,
            health_status=ContextHealthStatus.FORCE_RESET,
            metrics_snapshot=metrics.to_dict(),
            decision=ContextHealthDecision.FORCE_RESET,
            decision_reason=reason,
            blocking_metrics=blocking_metrics,
            latency_ms=latency_ms,
        )


# ============================================================================
# Receipt: Session Reset
# ============================================================================

class SessionResetReceipt(BaseReceipt):
    """
    Receipt emitted when a session is reset.

    This provides an audit trail of when and why sessions
    were reset, including final metrics before reset.

    Schema: context_health.v1
    """

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

    # Session identity
    old_session_id: str = Field(
        description="Session ID being reset"
    )
    new_session_id: Optional[str] = Field(
        default=None,
        description="New session ID (if continuation)"
    )

    # Reset details
    reset_reason: ResetReason = Field(
        description="Why the reset occurred"
    )
    reset_detail: str = Field(
        default="",
        description="Additional detail about the reset"
    )

    # Final metrics
    final_metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Metrics at time of reset"
    )

    # Session summary
    total_actions: int = Field(
        default=0,
        description="Total actions in the reset session"
    )
    total_high_risk_actions: int = Field(
        default=0,
        description="Total high-risk actions in the session"
    )
    session_duration_minutes: float = Field(
        default=0.0,
        description="How long the session lasted"
    )

    # Who triggered reset
    triggered_by: str = Field(
        default="system",
        description="Who triggered the reset (system/user/operator)"
    )

    @classmethod
    def create(
        cls,
        old_session_id: str,
        reset_reason: ResetReason,
        metrics: ContextHealthMetrics,
        reset_detail: str = "",
        new_session_id: Optional[str] = None,
        triggered_by: str = "system",
    ) -> "SessionResetReceipt":
        """Create a new session reset receipt."""
        now = datetime.now(timezone.utc)

        return cls(
            receipt_id=f"rcpt_reset_{old_session_id}_{now.strftime('%Y%m%dT%H%M%S')}",
            ts=now,
            old_session_id=old_session_id,
            new_session_id=new_session_id,
            reset_reason=reset_reason,
            reset_detail=reset_detail,
            final_metrics=metrics.to_dict(),
            total_actions=metrics.actions_since_reset,
            total_high_risk_actions=metrics.high_risk_action_count,
            session_duration_minutes=metrics.session_duration_minutes,
            triggered_by=triggered_by,
        )


__all__ = [
    # Enums
    "ContextHealthStatus",
    "ContextHealthDecision",
    "ResetReason",
    "HealthMetricStatus",
    # Models
    "ContextHealthMetrics",
    "ContextHealthThresholds",
    # Receipts
    "ContextHealthCheckReceipt",
    "ContextHealthVerdictReceipt",
    "SessionResetReceipt",
]
