"""
Shared trust computation for receipts.

Provides a unified TrustProfile that both CLI verifier and SedimentVault
can use, eliminating drift between "court-grade" definitions.

Usage:
    from receipts.trust import compute_trust_profile, TrustProfile
    
    profile = compute_trust_profile(receipt)
    if profile.court_grade:
        # Safe for immutable storage
    
    badge, tooltip = profile.to_badge()
    # Render in UI
"""

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


class TrustMode(Enum):
    """How/why verification was performed."""
    PROD = "prod"           # Full production verification
    MOCK = "mock"           # Mock TSA/Rekor (testing)
    OFFLINE = "offline"     # Offline by design (no network)
    DEGRADED = "degraded"   # Online attempted but failed/timed out


class TrustSource(Enum):
    """What the trust assessment is based on."""
    LIVE_VERIFY = "live_verify"  # Fresh verification run
    ANCHOR = "anchor"            # Trusting stored anchor status
    COMPUTED = "computed"        # Computed from raw fields (no verification)


@dataclass
class TrustProfile:
    """
    Compact trust status for surfacing in UI/vault.
    
    Unified semantics:
    - court_grade: True IFF hash + sig + TSA + Rekor all present, non-mock
    - mock: True if any proof step used mock data
    - mode: prod/mock/offline/degraded - why we're in this state
    - source: live_verify/anchor/computed - what this assessment is based on
    - stale: True when policy/TSA/Rekor roots no longer available locally
    
    Rule: mock → never court_grade (enforced)
    """
    court_grade: bool = False
    mock: bool = False
    missing: List[str] = field(default_factory=list)
    verified_online: bool = False
    mode: TrustMode = TrustMode.PROD
    source: TrustSource = TrustSource.COMPUTED
    stale: bool = False
    provenance: Dict[str, Any] = field(default_factory=dict)
    # Optional dignity score hook (for future COLD→FOSSIL gating)
    dignity_score: Optional[float] = None
    
    def __post_init__(self):
        # Enforce: mock → never court_grade
        if self.mock:
            self.court_grade = False
    
    def to_dict(self) -> Dict[str, Any]:
        result = {
            "court_grade": self.court_grade,
            "mock": self.mock,
            "missing": self.missing,
            "verified_online": self.verified_online,
            "mode": self.mode.value,
            "source": self.source.value,
            "stale": self.stale,
            "provenance": self.provenance,
        }
        if self.dignity_score is not None:
            result["dignity_score"] = self.dignity_score
        return result
    
    @classmethod
    def from_dict(cls, d: Dict[str, Any]) -> "TrustProfile":
        """Reconstruct from serialized dict."""
        return cls(
            court_grade=d.get("court_grade", False),
            mock=d.get("mock", False),
            missing=d.get("missing", []),
            verified_online=d.get("verified_online", False),
            mode=TrustMode(d.get("mode", "prod")),
            source=TrustSource(d.get("source", "computed")),
            stale=d.get("stale", False),
            provenance=d.get("provenance", {}),
            dignity_score=d.get("dignity_score"),
        )
    
    def to_badge(self) -> Tuple[str, str]:
        """
        Return (badge_text, tooltip) for UI display.
        
        Mapping:
        - court_grade && mode=prod: "Verified (court-grade)"
        - mock: "Verified locally (mock proofs)"
        - offline: "Verified locally (offline)"
        - degraded/stale: "Partial: proofs unavailable now"
        - missing items: "Partial: missing X, Y"
        """
        if self.court_grade and self.mode == TrustMode.PROD:
            return ("✅ Court-Grade", "All proofs verified: hash, signature, TSA timestamp, Rekor inclusion")
        elif self.mock:
            return ("⚡ Mock", "Verified locally; external proofs are mocked in this environment")
        elif self.mode == TrustMode.OFFLINE:
            return ("○ Offline", "Verified locally; external proof verification skipped (offline mode)")
        elif self.mode == TrustMode.DEGRADED or self.stale:
            return ("⚠ Partial", "External proofs unavailable now; may have been valid at issuance")
        elif self.missing:
            return ("⚠ Partial", f"Missing: {', '.join(self.missing)}")
        else:
            return ("✓ Verified", "Verified with available proofs")
    
    def meets_dignity_floor(self, floor: float = 0.15) -> bool:
        """Check if dignity score meets minimum threshold."""
        if self.dignity_score is None:
            return True  # No score = no enforcement
        return self.dignity_score >= floor
    
    @property
    def risk_level(self) -> str:
        """
        Lightweight risk summary for dashboards/filters.
        
        - court-grade → low
        - mock/offline modes → medium
        - missing proofs → medium
        - degraded or stale → high
        - otherwise unknown
        """
        if self.court_grade:
            return "low"
        if self.mock or self.mode in (TrustMode.MOCK, TrustMode.OFFLINE):
            return "medium"
        if self.missing:
            return "medium"
        if self.mode == TrustMode.DEGRADED or self.stale:
            return "high"
        return "unknown"


def _has_jcs_hash(receipt: Dict[str, Any]) -> bool:
    """Check if receipt has a JCS hash."""
    rh = receipt.get("receipt_hash", "")
    return bool(rh) and (rh.startswith("sha256:") or len(rh) >= 32)


def _has_signature(receipt: Dict[str, Any]) -> bool:
    """Check if receipt has a signature."""
    sigs = receipt.get("signatures", [])
    if sigs:
        return True
    sig = receipt.get("signature")
    if sig and isinstance(sig, dict) and sig.get("signature_b64"):
        return True
    return False


def _has_tsa(receipt: Dict[str, Any]) -> bool:
    """Check if receipt has TSA timestamp proof."""
    anchor = receipt.get("anchor", {})
    if anchor.get("tsa_token_b64") or anchor.get("tsa_timestamp"):
        return True
    tsa = receipt.get("tsa_timestamp", {})
    if isinstance(tsa, dict) and (tsa.get("token_b64") or tsa.get("token") or tsa.get("timestamp")):
        return True
    # Also check if tsa_timestamp is a string timestamp
    if isinstance(tsa, str) and tsa:
        return True
    return False


def _has_rekor(receipt: Dict[str, Any]) -> bool:
    """Check if receipt has Rekor inclusion proof."""
    anchor = receipt.get("anchor", {})
    if anchor.get("rekor_uuid") or anchor.get("rekor_log_index"):
        return True
    rekor = receipt.get("rekor_entry", {})
    if rekor.get("uuid") or rekor.get("log_index"):
        return True
    return False


def _is_mock(receipt: Dict[str, Any]) -> bool:
    """Check if receipt uses mock proofs."""
    anchor = receipt.get("anchor", {})
    
    # Explicit is_mock flag
    if anchor.get("is_mock", False):
        return True
    
    # Mock-prefixed UUIDs
    rekor_uuid = anchor.get("rekor_uuid", "")
    if rekor_uuid.startswith("mock-") or rekor_uuid.startswith("mock_"):
        return True
    
    # Top-level tsa_timestamp mock check
    tsa = receipt.get("tsa_timestamp", {})
    if tsa.get("is_mock", False):
        return True
    
    return False


def _get_dignity_score(receipt: Dict[str, Any]) -> Optional[float]:
    """Extract dignity/coherence score if present."""
    # Check various possible locations
    metrics = receipt.get("metrics", {})
    if "dignity" in metrics:
        return float(metrics["dignity"])
    if "coherence" in metrics:
        return float(metrics["coherence"])
    
    # Check risk assessment
    risk = receipt.get("risk", {})
    if "dignity_score" in risk:
        return float(risk["dignity_score"])
    
    # Check top-level dignity
    if "dignity" in receipt:
        return float(receipt["dignity"])
    
    return None


def compute_trust_profile(
    receipt: Dict[str, Any],
    *,
    verified_online: bool = False,
    mode: Optional[TrustMode] = None,
    source: TrustSource = TrustSource.COMPUTED,
    stale: bool = False,
    provenance: Optional[Dict[str, Any]] = None,
) -> TrustProfile:
    """
    Compute TrustProfile from receipt fields.
    
    This is the canonical trust evaluation function. Both CLI verifier
    and SedimentVault should use this to ensure consistent "court-grade"
    definitions.
    
    Args:
        receipt: Receipt dict to evaluate
        verified_online: Whether TSA/Rekor were verified online
        mode: Override trust mode (auto-detected if None)
        source: Source of trust assessment
        stale: Whether policy/roots are stale
        provenance: Additional provenance info
    
    Returns:
        TrustProfile with unified trust assessment
    
    Court-grade requires:
        - JCS hash present
        - Signature present  
        - TSA timestamp present
        - Rekor inclusion present
        - NOT mock
    """
    has_hash = _has_jcs_hash(receipt)
    has_sig = _has_signature(receipt)
    has_tsa = _has_tsa(receipt)
    has_rekor = _has_rekor(receipt)
    is_mock = _is_mock(receipt)
    dignity = _get_dignity_score(receipt)
    
    # Build missing list
    missing = []
    if not has_hash:
        missing.append("JCS Hash")
    if not has_sig:
        missing.append("Signature")
    if not has_tsa:
        missing.append("TSA Timestamp")
    if not has_rekor:
        missing.append("Rekor Inclusion")
    
    # Court-grade: all proofs present AND not mock
    court_grade = (
        has_hash and has_sig and has_tsa and has_rekor and not is_mock
    )
    
    # Auto-detect mode if not provided
    if mode is None:
        if is_mock:
            mode = TrustMode.MOCK
        else:
            mode = TrustMode.PROD
    
    # Check for pre-computed trust_profile on receipt
    existing = receipt.get("trust_profile") or receipt.get("_trust_profile")
    if existing and isinstance(existing, dict):
        provenance = provenance or {}
        provenance["merged_from_receipt"] = True
        # Merge verified_online from existing if present
        if existing.get("verified_online"):
            verified_online = True
        if dignity is None and existing.get("dignity_score") is not None:
            try:
                dignity = float(existing["dignity_score"])
            except (TypeError, ValueError):
                pass
    
    return TrustProfile(
        court_grade=court_grade,
        mock=is_mock,
        missing=missing,
        verified_online=verified_online,
        mode=mode,
        source=source,
        stale=stale,
        provenance=provenance or {},
        dignity_score=dignity,
    )


def trust_profile_from_anchor(anchor: Dict[str, Any]) -> TrustProfile:
    """
    Build TrustProfile from an anchor record.
    
    Useful when you want to trust the anchor's stored status
    rather than re-verifying from the receipt.
    """
    has_hash = bool(anchor.get("receipt_hash"))
    has_sig = bool(anchor.get("signature_b64"))
    has_tsa = bool(anchor.get("tsa_token_b64") or anchor.get("tsa_timestamp"))
    has_rekor = bool(anchor.get("rekor_uuid") or anchor.get("rekor_log_index"))
    is_mock = anchor.get("is_mock", False)
    
    # Check for mock prefix
    rekor_uuid = anchor.get("rekor_uuid", "")
    if rekor_uuid.startswith("mock-") or rekor_uuid.startswith("mock_"):
        is_mock = True
    
    missing = []
    if not has_hash:
        missing.append("JCS Hash")
    if not has_sig:
        missing.append("Signature")
    if not has_tsa:
        missing.append("TSA Timestamp")
    if not has_rekor:
        missing.append("Rekor Inclusion")
    
    court_grade = has_hash and has_sig and has_tsa and has_rekor and not is_mock
    
    return TrustProfile(
        court_grade=court_grade,
        mock=is_mock,
        missing=missing,
        verified_online=False,  # Anchor-based = not live verified
        mode=TrustMode.MOCK if is_mock else TrustMode.PROD,
        source=TrustSource.ANCHOR,
        stale=False,
        provenance={
            "from_anchor": True,
            "rekor_log_index": anchor.get("rekor_log_index"),
            "tsa_timestamp": anchor.get("tsa_timestamp"),
        },
    )
