"""
Unified Receipt Verification

Verifies a complete ReceiptV1 by:
1. Checking structural validity
2. Validating tri-temporal ordering
3. Checking dignity constraints
4. Verifying all present proofs
5. Confirming proof contract is satisfied
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from typing import Dict, List, TYPE_CHECKING

from receipts.verifiers.verdict import ProofVerdict, VerdictStatus
from receipts.verifiers.content_digest import verify_content_digest
from receipts.verifiers.local_signature import verify_local_signature
from receipts.verifiers.tsa_rfc3161 import verify_tsa_rfc3161
from receipts.verifiers.transparency_log import verify_transparency_log

if TYPE_CHECKING:
    from receipts.models_v1 import ReceiptV1

logger = logging.getLogger(__name__)


# Mapping of proof kinds to their verifiers
PROOF_VERIFIERS = {
    "content-digest": verify_content_digest,
    "local-signature": verify_local_signature,
    "tsa-rfc3161": verify_tsa_rfc3161,
    "transparency-log": verify_transparency_log,
    # Future:
    # "enclave-attestation": verify_enclave_attestation,
    # "pq-signature": verify_pq_signature,
    # "witness-signature": verify_witness_signature,
}


@dataclass
class ReceiptVerificationResult:
    """Complete verification result for a receipt."""
    receipt_id: str
    profile_id: str
    attestation_level: str

    # Overall status
    status: str  # "verified" | "failed" | "degraded"

    # Individual proof results
    proof_results: Dict[str, ProofVerdict] = field(default_factory=dict)

    # Constraint checks
    tri_temporal_ok: bool = True
    dignity_ok: bool = True
    contract_satisfied: bool = True

    # Issues
    errors: List[str] = field(default_factory=list)
    warnings: List[str] = field(default_factory=list)

    @property
    def is_verified(self) -> bool:
        return self.status == "verified"

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON output."""
        return {
            "status": self.status,
            "receipt_id": self.receipt_id,
            "profile_id": self.profile_id,
            "attestation_level": self.attestation_level,
            "proof_results": {
                k: v.to_dict() for k, v in self.proof_results.items()
            },
            "tri_temporal_ok": self.tri_temporal_ok,
            "dignity_ok": self.dignity_ok,
            "contract_satisfied": self.contract_satisfied,
            "errors": self.errors,
            "warnings": self.warnings,
        }

    def to_human_string(self) -> str:
        """Format as human-readable string."""
        lines = [
            f"Receipt: {self.receipt_id}",
            "",
            f"Profile: {self.profile_id} ({self.attestation_level})",
        ]

        # Status with symbol
        status_symbol = {
            "verified": "✅ VERIFIED",
            "failed": "❌ FAILED",
            "degraded": "⚠️ DEGRADED",
        }.get(self.status, self.status)
        lines.append(f"Status : {status_symbol}")
        lines.append("")

        # Proof results
        lines.append("Proofs:")
        for kind, verdict in self.proof_results.items():
            symbol = {
                VerdictStatus.OK: "✅",
                VerdictStatus.FAIL: "❌",
                VerdictStatus.SKIPPED: "⏭️",
            }.get(verdict.status, "?")
            reason = f" ({verdict.reason})" if verdict.reason else ""
            lines.append(f"  - {kind:20} {symbol}{reason}")

        lines.append("")

        # Constraints
        lines.append(f"Tri-temporal: {'✅' if self.tri_temporal_ok else '❌'} E <= O <= R")
        lines.append(f"Dignity     : {'✅' if self.dignity_ok else '❌'}")

        # Errors/warnings
        if self.errors:
            lines.append("")
            lines.append("Errors:")
            for err in self.errors:
                lines.append(f"  - {err}")

        if self.warnings:
            lines.append("")
            lines.append("Warnings:")
            for warn in self.warnings:
                lines.append(f"  - {warn}")

        return "\n".join(lines)


def verify_receipt(receipt: "ReceiptV1") -> ReceiptVerificationResult:
    """
    Perform complete verification of a ReceiptV1.

    This is the main entry point for receipt verification. It:
    1. Checks tri-temporal ordering (E <= O <= R)
    2. Checks dignity constraints against profile
    3. Verifies all present proofs
    4. Confirms the proof contract is satisfied

    Args:
        receipt: The ReceiptV1 to verify

    Returns:
        ReceiptVerificationResult with detailed status
    """
    result = ReceiptVerificationResult(
        receipt_id=receipt.envelope.receipt_id,
        profile_id=receipt.profile_snapshot.proof_profile_id,
        attestation_level=receipt.envelope.attestation_level,
        status="verified",  # Optimistic; will downgrade if issues found
    )

    # 1. Check tri-temporal ordering
    env = receipt.envelope
    if receipt.profile_snapshot.enforce_tri_temporal:
        if env.event_time > env.observed_time:
            result.tri_temporal_ok = False
            result.errors.append(f"Temporal violation: event_time > observed_time")
        if env.observed_time > env.receipt_time:
            result.tri_temporal_ok = False
            result.errors.append(f"Temporal violation: observed_time > receipt_time")

    # 2. Check dignity constraint
    if env.dignity_score < receipt.profile_snapshot.min_dignity:
        result.dignity_ok = False
        result.errors.append(
            f"Dignity violation: {env.dignity_score:.2f} < {receipt.profile_snapshot.min_dignity:.2f}"
        )

    # 3. Verify each present proof
    for proof in receipt.proofs:
        kind = proof.kind
        verifier = PROOF_VERIFIERS.get(kind)

        if verifier is None:
            result.warnings.append(f"No verifier for proof kind: {kind}")
            result.proof_results[kind] = ProofVerdict.skipped(
                kind=kind,
                reason="No verifier available",
            )
            continue

        try:
            verdict = verifier(receipt)
            result.proof_results[kind] = verdict

            if verdict.is_fail:
                result.errors.append(f"Proof {kind} failed: {verdict.reason}")
        except Exception as e:
            logger.exception(f"Verifier error for {kind}")
            result.proof_results[kind] = ProofVerdict.fail(
                kind=kind,
                reason=f"Verifier exception: {e}",
            )
            result.errors.append(f"Proof {kind} error: {e}")

    # 4. Check proof contract satisfaction
    required = set(receipt.profile_snapshot.required_proofs)
    present = set(result.proof_results.keys())
    present_ok = {k for k, v in result.proof_results.items() if v.is_ok}

    missing_required = required - present
    failed_required = required - present_ok

    if missing_required:
        result.contract_satisfied = False
        result.errors.append(f"Missing required proofs: {missing_required}")

    if failed_required:
        result.contract_satisfied = False
        # Already logged individual failures above

    # 5. Cross-check with receipt's own proof_status
    if receipt.proof_status.missing:
        result.warnings.append(
            f"Receipt marked as degraded with {len(receipt.proof_status.missing)} missing proofs"
        )

    # 6. Determine final status
    if result.errors:
        # Check if it's just degraded vs hard failure
        all_proofs_present_ok = not missing_required and not failed_required
        constraints_ok = result.tri_temporal_ok and result.dignity_ok

        if constraints_ok and receipt.proof_status.is_degraded and all_proofs_present_ok:
            result.status = "degraded"
        else:
            result.status = "failed"
    else:
        result.status = "verified"

    return result
