"""
Kill Switch Receipts: Emergency capability revocation with graduated response modes.

This module implements the KillSwitchReceipt described in the patent specification,
providing three graduated modes for emergency capability revocation:

- SOFT: Capability expiration or revocation (blocks new authorizations)
- HARD: Module or tool quarantine (immediate isolation, state preserved)
- EMERGENCY: Session abort / execution halt (system-wide stop, no state preservation)

These receipts enable rapid incident response while maintaining audit trail
and supporting graduated escalation.

Schema: kill_switch.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, model_validator
from receipts.base import BaseReceipt, Domain, _normalise_utc_millis


class KillSwitchMode(str, Enum):
    """
    Kill switch response modes with increasing severity.

    SOFT: Capability expiration or revocation.
          Effect: New authorizations blocked, in-flight may complete.
          Use for: Non-critical issues, planned capability deprecation.
          Constraints: May have expires_at, cannot have state_snapshot_id.

    HARD: Module or tool quarantine.
          Effect: Immediate isolation, state preserved for recovery.
          Use for: Security incidents, significant capability misuse.
          Constraints: May have expires_at (time-boxed quarantine) and/or state_snapshot_id.

    EMERGENCY: Session abort / execution halt.
               Effect: Immediate stop, no state preservation, system-wide.
               Use for: Active exploitation, catastrophic safety failures.
               Constraints: Cannot have expires_at or state_snapshot_id (permanent, no recovery path).
    """
    SOFT = "SOFT"
    HARD = "HARD"
    EMERGENCY = "EMERGENCY"


class KillSwitchTargetType(str, Enum):
    """Types of targets that can be killed."""
    CAPABILITY = "capability"
    TOOL = "tool"
    AGENT = "agent"
    MODULE = "module"
    SYSTEM = "system"


class RecoveryStatus(str, Enum):
    """Status of recovery after kill switch deactivation."""
    FULL = "FULL"           # Complete recovery, no degradation
    PARTIAL = "PARTIAL"     # Some functionality restored
    DEGRADED = "DEGRADED"   # Operating with reduced capability


class KeyRevocationReason(str, Enum):
    """Machine-readable reasons for key revocation."""
    COMPROMISED = "COMPROMISED"     # Key may have been exposed
    EXPIRED = "EXPIRED"             # Key reached end of life
    SUPERSEDED = "SUPERSEDED"       # Replaced by new key (rotation)
    ADMIN_ACTION = "ADMIN_ACTION"   # Administrative revocation


class KillSwitchTarget(BaseModel):
    """Specification of what the kill switch affects."""

    target_type: KillSwitchTargetType = Field(
        description="Type of target: capability, tool, agent, module, system"
    )

    target_id: str = Field(
        description="Identifier of the specific target to kill"
    )

    scope: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Optional scope restriction for the kill switch"
    )


class KillSwitchReceipt(BaseReceipt):
    """
    Receipt emitted when a kill switch is activated.

    The kill switch provides graduated emergency response per the patent:
    - SOFT: Capability expiration or revocation
    - HARD: Module or tool quarantine
    - EMERGENCY: Session abort / execution halt

    This receipt creates an immutable audit record of the activation,
    supporting incident response and post-incident analysis.

    Mode Invariants:
    - EMERGENCY cannot have expires_at (permanent until explicitly deactivated)
    - EMERGENCY cannot have state_snapshot_id (no recovery path)
    - SOFT cannot have state_snapshot_id (no state preservation in soft mode)

    Schema: kill_switch.v1
    """

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

    # Kill switch identification
    kill_switch_id: str = Field(
        description="Unique identifier for this kill switch activation"
    )

    # Mode determines severity of response
    mode: KillSwitchMode = Field(
        description="Response mode: SOFT, HARD, or EMERGENCY"
    )

    # What is being killed
    target: KillSwitchTarget = Field(
        description="Specification of what the kill switch affects"
    )

    # Who activated it
    activated_by: str = Field(
        description="Principal who activated the kill switch (e.g., human:tim, service:guardian)"
    )

    # Why it was activated (triggering condition per Claim 18)
    reason: str = Field(
        description="Human-readable reason for activation (triggering condition)"
    )
    reason_code: Optional[str] = Field(
        default=None,
        description="Machine-readable reason code (e.g., SECURITY_INCIDENT, SAFETY_VIOLATION)"
    )

    # Incident linkage
    incident_id: Optional[str] = Field(
        default=None,
        description="Optional link to related incident record"
    )

    # Timing
    activated_at: datetime = Field(
        description="When the kill switch was activated (normalized to UTC)"
    )
    expires_at: Optional[datetime] = Field(
        default=None,
        description="Optional auto-expiry for SOFT or HARD modes (not allowed for EMERGENCY)"
    )

    # Propagation tracking
    propagated_to: List[str] = Field(
        default_factory=list,
        description="List of subsystems notified of this kill switch"
    )

    # State preservation (for HARD mode)
    state_snapshot_id: Optional[str] = Field(
        default=None,
        description="ID of state snapshot taken before termination (HARD mode only)"
    )

    @model_validator(mode="after")
    def _normalise_and_validate(self) -> "KillSwitchReceipt":
        """
        Normalize timestamps to UTC and enforce mode invariants.

        Invariants:
        - EMERGENCY mode cannot have auto-expiry (permanent until deactivated)
        - SOFT mode cannot have state_snapshot_id (no state preservation)
        """
        # Normalize timestamps to UTC with millisecond precision
        object.__setattr__(self, "activated_at", _normalise_utc_millis(self.activated_at))
        if self.expires_at is not None:
            object.__setattr__(self, "expires_at", _normalise_utc_millis(self.expires_at))

        # Enforce mode invariants
        if self.mode == KillSwitchMode.EMERGENCY:
            if self.expires_at is not None:
                raise ValueError(
                    "EMERGENCY mode kill switches cannot have auto-expiry "
                    "(they are permanent until explicitly deactivated)"
                )
            if self.state_snapshot_id is not None:
                raise ValueError(
                    "EMERGENCY mode cannot have state_snapshot_id "
                    "(use HARD mode for recoverable halt with state preservation)"
                )
        if self.mode == KillSwitchMode.SOFT and self.state_snapshot_id is not None:
            raise ValueError(
                "SOFT mode does not take state snapshots "
                "(use HARD mode for state-preserving quarantine)"
            )

        return self


class KillSwitchDeactivationReceipt(BaseReceipt):
    """
    Receipt emitted when a kill switch is deactivated.

    This provides audit trail for recovery from kill switch events.
    """

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

    # Link to original activation
    kill_switch_id: str = Field(
        description="ID of the kill switch being deactivated"
    )
    kill_switch_receipt_id: str = Field(
        description="Receipt ID of the original activation"
    )

    # Deactivation details
    deactivated_by: str = Field(
        description="Principal who deactivated the kill switch"
    )
    deactivated_at: datetime = Field(
        description="When the kill switch was deactivated (normalized to UTC)"
    )

    # Recovery status
    recovery_status: RecoveryStatus = Field(
        description="Status of recovery: FULL, PARTIAL, DEGRADED"
    )
    recovery_notes: Optional[str] = Field(
        default=None,
        description="Notes on recovery process or limitations"
    )

    @model_validator(mode="after")
    def _normalise_timestamps(self) -> "KillSwitchDeactivationReceipt":
        """Normalize deactivated_at to UTC with millisecond precision."""
        object.__setattr__(self, "deactivated_at", _normalise_utc_millis(self.deactivated_at))
        return self


class KeyRevocationReceipt(BaseReceipt):
    """
    Receipt emitted when a signing key is revoked.

    This invalidates all capabilities signed with the revoked key,
    providing cryptographic incident response capability.
    """

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

    # Key identification
    key_id: str = Field(
        description="Identifier of the revoked key"
    )
    key_fingerprint: str = Field(
        description="Fingerprint of the revoked key (for verification)"
    )

    # Revocation details
    revoked_by: str = Field(
        description="Principal who revoked the key"
    )
    revoked_at: datetime = Field(
        description="When the key was revoked (normalized to UTC)"
    )

    # Reason
    reason: str = Field(
        description="Reason for revocation"
    )
    reason_code: KeyRevocationReason = Field(
        description="Machine-readable reason: COMPROMISED, EXPIRED, SUPERSEDED, ADMIN_ACTION"
    )

    # Impact tracking
    affected_capability_count: int = Field(
        default=0,
        description="Number of capabilities invalidated by this revocation"
    )

    # Successor key (if rotating)
    successor_key_id: Optional[str] = Field(
        default=None,
        description="ID of replacement key if this is a rotation"
    )

    @model_validator(mode="after")
    def _normalise_timestamps(self) -> "KeyRevocationReceipt":
        """Normalize revoked_at to UTC with millisecond precision."""
        object.__setattr__(self, "revoked_at", _normalise_utc_millis(self.revoked_at))
        return self


def create_kill_switch_receipt(
    target_type: KillSwitchTargetType,
    target_id: str,
    mode: KillSwitchMode,
    activated_by: str,
    reason: str,
    reason_code: Optional[str] = None,
    scope: Optional[Dict[str, Any]] = None,
    incident_id: Optional[str] = None,
    expires_at: Optional[datetime] = None,
    state_snapshot_id: Optional[str] = None,
) -> KillSwitchReceipt:
    """
    Create a KillSwitchReceipt with proper defaults.

    Args:
        target_type: Type of target (capability, tool, agent, module, system)
        target_id: Identifier of the target
        mode: Response mode (SOFT, HARD, EMERGENCY)
        activated_by: Principal activating the kill switch
        reason: Human-readable reason (triggering condition)
        reason_code: Optional machine-readable code
        scope: Optional scope restriction
        incident_id: Optional link to incident
        expires_at: Optional auto-expiry (SOFT mode only, raises for EMERGENCY)
        state_snapshot_id: Optional state snapshot ID (HARD mode only)

    Returns:
        KillSwitchReceipt ready for signing and storage

    Raises:
        ValueError: If mode invariants are violated
    """
    now = datetime.now(timezone.utc)
    kill_switch_id = f"ks_{now.strftime('%Y%m%dT%H%M%SZ')}_{uuid.uuid4().hex[:8]}"
    receipt_id = f"ksr_{uuid.uuid4().hex[:16]}"

    return KillSwitchReceipt(
        receipt_id=receipt_id,
        kill_switch_id=kill_switch_id,
        mode=mode,
        target=KillSwitchTarget(
            target_type=target_type,
            target_id=target_id,
            scope=scope,
        ),
        activated_by=activated_by,
        reason=reason,
        reason_code=reason_code,
        incident_id=incident_id,
        activated_at=now,
        expires_at=expires_at,
        state_snapshot_id=state_snapshot_id,
    )


def create_key_revocation_receipt(
    key_id: str,
    key_fingerprint: str,
    revoked_by: str,
    reason: str,
    reason_code: KeyRevocationReason,
    affected_capability_count: int = 0,
    successor_key_id: Optional[str] = None,
) -> KeyRevocationReceipt:
    """
    Create a KeyRevocationReceipt with proper defaults.

    Args:
        key_id: Identifier of the key being revoked
        key_fingerprint: Fingerprint of the key
        revoked_by: Principal revoking the key
        reason: Human-readable reason
        reason_code: Machine-readable reason code
        affected_capability_count: Number of capabilities invalidated
        successor_key_id: ID of replacement key if rotating

    Returns:
        KeyRevocationReceipt ready for signing and storage
    """
    now = datetime.now(timezone.utc)
    receipt_id = f"krr_{uuid.uuid4().hex[:16]}"

    return KeyRevocationReceipt(
        receipt_id=receipt_id,
        key_id=key_id,
        key_fingerprint=key_fingerprint,
        revoked_by=revoked_by,
        revoked_at=now,
        reason=reason,
        reason_code=reason_code,
        affected_capability_count=affected_capability_count,
        successor_key_id=successor_key_id,
    )


__all__ = [
    "KillSwitchMode",
    "KillSwitchTargetType",
    "RecoveryStatus",
    "KeyRevocationReason",
    "KillSwitchTarget",
    "KillSwitchReceipt",
    "KillSwitchDeactivationReceipt",
    "KeyRevocationReceipt",
    "create_kill_switch_receipt",
    "create_key_revocation_receipt",
]
