# SPDX-License-Identifier: Apache-2.0
"""Receipt: content-addressed decision records.

Every gate decision produces a Receipt. Receipts are content-addressed:
same subject + same evidence + same gate = same receipt_id.
Timestamp is metadata, not identity.
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any

from .canonical import canonical_json, content_hash, subject_hash

RECEIPT_SCHEMA_VERSION = 1

VERDICT_PASS = "pass"
VERDICT_WARN = "warn"
VERDICT_BLOCK = "block"
VERDICT_OBSERVE = "observe"
VALID_VERDICTS = frozenset({VERDICT_PASS, VERDICT_WARN, VERDICT_BLOCK, VERDICT_OBSERVE})


@dataclass(frozen=True)
class Receipt:
    """A content-addressed decision receipt.

    receipt_id = H(schema_version + gate + subject_hash + evidence_hash)
    Timestamp is metadata — NOT part of receipt_id.
    """

    receipt_id: str
    schema_version: int
    timestamp: str
    gate: str
    verdict: str
    subject_hash: str
    evidence_hash: str

    def __post_init__(self) -> None:
        if self.verdict not in VALID_VERDICTS:
            raise ValueError(
                f"Invalid verdict {self.verdict!r}; "
                f"must be one of {sorted(VALID_VERDICTS)}"
            )

    def to_dict(self) -> dict[str, Any]:
        return {
            "receipt_id": self.receipt_id,
            "schema_version": self.schema_version,
            "timestamp": self.timestamp,
            "gate": self.gate,
            "verdict": self.verdict,
            "subject_hash": self.subject_hash,
            "evidence_hash": self.evidence_hash,
        }

    @classmethod
    def from_dict(cls, d: dict[str, Any]) -> Receipt:
        v = d.get("schema_version", 0)
        if v > RECEIPT_SCHEMA_VERSION:
            raise ValueError(
                f"Receipt schema version {v} is newer than supported "
                f"({RECEIPT_SCHEMA_VERSION}). Upgrade nlai."
            )
        return cls(
            receipt_id=d["receipt_id"],
            schema_version=d.get("schema_version", RECEIPT_SCHEMA_VERSION),
            timestamp=d["timestamp"],
            gate=d["gate"],
            verdict=d["verdict"],
            subject_hash=d["subject_hash"],
            evidence_hash=d["evidence_hash"],
        )


def create_receipt(
    *,
    gate: str,
    verdict: str,
    subject_kind: str,
    subject_bytes: bytes,
    evidence: dict[str, Any] | None = None,
) -> Receipt:
    """Create a content-addressed receipt.

    Args:
        gate: Which gate produced this receipt (e.g., "nlai_gate").
        verdict: One of VALID_VERDICTS.
        subject_kind: Kind tag for subject hashing (e.g., "text").
        subject_bytes: The raw bytes being checked.
        evidence: Optional evidence dict (hashed, not stored in receipt).
    """
    s_hash = subject_hash(subject_kind, subject_bytes)
    e_hash = content_hash(canonical_json(evidence or {}))

    # Content-addressed identity: same inputs = same receipt_id
    payload = f"{RECEIPT_SCHEMA_VERSION}:{gate}:{s_hash}:{e_hash}"
    r_id = content_hash(payload.encode("utf-8"))

    return Receipt(
        receipt_id=r_id,
        schema_version=RECEIPT_SCHEMA_VERSION,
        timestamp=datetime.now(timezone.utc).isoformat(),
        gate=gate,
        verdict=verdict,
        subject_hash=s_hash,
        evidence_hash=e_hash,
    )


def verify_receipt(receipt: Receipt) -> bool:
    """Verify a receipt's content-addressed identity is consistent."""
    payload = (
        f"{receipt.schema_version}:{receipt.gate}:"
        f"{receipt.subject_hash}:{receipt.evidence_hash}"
    )
    expected = content_hash(payload.encode("utf-8"))
    return receipt.receipt_id == expected
