"""
Replay Passport Service

Creates and manages ReplayPassportV2 instances for TBR gate compliance.

Regulated and court-tier decisions require a replay_passport_id to:
1. Enable decision replay (same inputs → same decision)
2. Track guardian verdict history
3. Link decisions to their lineage chain

Usage:
    from receipts.passport_service import PassportService

    service = PassportService()

    # Create passport for a decision
    passport = service.create_passport(
        case_id="PA-2024-001",
        suggestion={"assessment": "...", "plan": [...]},
        patient_context={"demographics": {...}},
        proof_tier="regulated",
    )

    # Lookup existing passport
    passport = service.get_passport(passport_id="pp-PA-2024-001")
"""

from __future__ import annotations

import logging
import threading
import uuid
from datetime import datetime, timezone
from typing import Any, Dict, Optional

from receipts.domains.replay_passport_v2 import PassportLineage, ReplayPassportV2

logger = logging.getLogger(__name__)

# Thread-safe in-memory passport store
_passport_store: Dict[str, ReplayPassportV2] = {}
_passport_lock = threading.Lock()


def _generate_passport_id(case_id: Optional[str], domain: str = "clinical") -> str:
    """
    Generate canonical passport_id from case_id.

    Format: pp-{domain}-{case_id} or pp-{uuid} if no case_id.

    Args:
        case_id: Case/PA identifier
        domain: Domain namespace

    Returns:
        Canonical passport_id
    """
    if case_id:
        # Remove any existing domain prefix
        clean_case = case_id.split(":")[-1] if ":" in case_id else case_id
        return f"pp-{domain}-{clean_case}"
    return f"pp-{uuid.uuid4().hex[:12]}"


class PassportService:
    """
    Service for creating and managing ReplayPassportV2 instances.

    Thread-safe with in-memory storage.
    """

    def __init__(self):
        """Initialize passport service."""
        pass

    def create_passport(
        self,
        case_id: Optional[str],
        suggestion: Dict[str, Any],
        patient_context: Dict[str, Any],
        proof_tier: str = "regulated",
        decision_kind: str = "production",
        snapshot_hash: Optional[str] = None,
    ) -> ReplayPassportV2:
        """
        Create a new ReplayPassportV2 for a decision.

        Args:
            case_id: Case/PA identifier
            suggestion: Clinical suggestion
            patient_context: Patient context
            proof_tier: Proof tier (core|regulated|court)
            decision_kind: Decision kind (production|shadow|...)
            snapshot_hash: Optional snapshot hash for court tier

        Returns:
            New ReplayPassportV2 instance
        """
        passport_id = _generate_passport_id(case_id, domain="clinical")

        # Compute content hashes for replay verification
        try:
            from receipts.tbr_crypto import compute_content_hash
            suggestion_hash = compute_content_hash(suggestion)
            context_hash = compute_content_hash(patient_context)
        except ImportError:
            suggestion_hash = None
            context_hash = None

        passport = ReplayPassportV2(
            request_id=passport_id,
            passport_id=passport_id,
            vault_id=case_id,
            guardian_verdict=None,
            lineage=PassportLineage(),
            metadata={
                "case_id": case_id,
                "proof_tier": proof_tier,
                "decision_kind": decision_kind,
                "created_at": datetime.now(timezone.utc).isoformat(),
                "suggestion_hash": suggestion_hash,
                "context_hash": context_hash,
                "snapshot_hash": snapshot_hash,
            },
        )

        # Store passport
        with _passport_lock:
            _passport_store[passport_id] = passport

        logger.info(
            "Created passport: id=%s case_id=%s proof_tier=%s",
            passport_id,
            case_id,
            proof_tier,
        )

        return passport

    def get_passport(self, passport_id: str) -> Optional[ReplayPassportV2]:
        """
        Lookup existing passport by ID.

        Args:
            passport_id: Passport identifier

        Returns:
            ReplayPassportV2 if found, None otherwise
        """
        with _passport_lock:
            return _passport_store.get(passport_id)

    def get_or_create_passport(
        self,
        case_id: Optional[str],
        suggestion: Dict[str, Any],
        patient_context: Dict[str, Any],
        proof_tier: str = "regulated",
        decision_kind: str = "production",
        snapshot_hash: Optional[str] = None,
    ) -> ReplayPassportV2:
        """
        Get existing passport or create new one.

        If case_id maps to an existing passport, returns it.
        Otherwise creates a new passport.

        Args:
            case_id: Case/PA identifier
            suggestion: Clinical suggestion
            patient_context: Patient context
            proof_tier: Proof tier
            decision_kind: Decision kind
            snapshot_hash: Optional snapshot hash

        Returns:
            ReplayPassportV2 instance
        """
        # Generate expected passport_id
        passport_id = _generate_passport_id(case_id, domain="clinical")

        # Check if exists
        existing = self.get_passport(passport_id)
        if existing:
            logger.debug("Found existing passport: %s", passport_id)
            return existing

        # Create new
        return self.create_passport(
            case_id=case_id,
            suggestion=suggestion,
            patient_context=patient_context,
            proof_tier=proof_tier,
            decision_kind=decision_kind,
            snapshot_hash=snapshot_hash,
        )

    def update_passport_verdict(
        self,
        passport_id: str,
        allowed: bool,
        outcome: str,
        reasons: Optional[list] = None,
        guardian_receipt_id: Optional[str] = None,
    ) -> Optional[ReplayPassportV2]:
        """
        Update passport with guardian verdict.

        Args:
            passport_id: Passport identifier
            allowed: Whether request was allowed
            outcome: Decision outcome (APPROVE, REFUSE, etc.)
            reasons: Reasons for decision
            guardian_receipt_id: Receipt ID to add to lineage

        Returns:
            Updated passport, or None if not found
        """
        from receipts.domains.replay_passport_v2 import GuardianVerdictSummary

        with _passport_lock:
            passport = _passport_store.get(passport_id)
            if not passport:
                logger.warning("Passport not found for verdict update: %s", passport_id)
                return None

            # Update verdict
            passport.guardian_verdict = GuardianVerdictSummary(
                allowed=allowed,
                outcome=outcome,
                reason="; ".join(reasons) if reasons else None,
                policy="guardian_service",
                metadata={"updated_at": datetime.now(timezone.utc).isoformat()},
            )

            # Update lineage
            if guardian_receipt_id:
                passport.lineage.guardian_receipt_ids.append(guardian_receipt_id)

            logger.info(
                "Updated passport verdict: id=%s allowed=%s outcome=%s",
                passport_id,
                allowed,
                outcome,
            )

            return passport

    def list_passports(self, limit: int = 100) -> list:
        """List recent passports (most recent first)."""
        with _passport_lock:
            passports = list(_passport_store.values())
        # Sort by created_at descending
        passports.sort(
            key=lambda p: p.metadata.get("created_at", ""),
            reverse=True,
        )
        return passports[:limit]

    def clear(self):
        """Clear all passports (for testing)."""
        with _passport_lock:
            _passport_store.clear()


# Singleton instance
_default_service: Optional[PassportService] = None


def get_passport_service() -> PassportService:
    """Get or create default passport service."""
    global _default_service
    if _default_service is None:
        _default_service = PassportService()
    return _default_service


# Convenience functions
def create_passport_for_decision(
    case_id: Optional[str],
    suggestion: Dict[str, Any],
    patient_context: Dict[str, Any],
    proof_tier: str = "regulated",
) -> str:
    """
    Create passport and return passport_id.

    Convenience function for Guardian service integration.

    Args:
        case_id: Case/PA identifier
        suggestion: Clinical suggestion
        patient_context: Patient context
        proof_tier: Proof tier

    Returns:
        passport_id string
    """
    service = get_passport_service()
    passport = service.get_or_create_passport(
        case_id=case_id,
        suggestion=suggestion,
        patient_context=patient_context,
        proof_tier=proof_tier,
    )
    return passport.passport_id
