# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-honor-a-cross-org-action-receipt-signed-under-a-rotated-key-whose-chain-of-custody-is-not-anchored-in-the.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-honor-a-cross-org-action-receipt-signed-under-a-rotated-key-whose-chain-of-custody-is-not-anchored-in-the.warrant.json
# Markdown SHA-256:   94937c61ee4459ddf859f9047795faba843a5f9744cc7426163f88bc2e2cb684
# Code AST SHA-256:   530053db1ec3b4172f00fe6d5a12d51695867c05a04498bd20ca3b108745f1dc
# Verified Classes:   UnanchoredReceiptError, SignedTreeHead, KeyBinding, ActionReceipt
# Defensive Guards:   8 fail-closed invariants (zero external deps)

import hashlib
import time
from dataclasses import dataclass
from typing import Optional


class UnanchoredReceiptError(RuntimeError):
    """Raised when a receipt is signature-valid but its key binding is not
    anchored in a witnessed signed tree head prior to the signing time."""


@dataclass(frozen=True)
class SignedTreeHead:
    tree_size: int
    root_hash: bytes
    timestamp: float
    witnessed_at: float  # when *we* observed/cosigned it


@dataclass(frozen=True)
class KeyBinding:
    principal: str
    key_id: str
    valid_from: float
    valid_to: Optional[float]
    inclusion_index: int
    sths: tuple  # SignedTreeHeads the binding is proven against


@dataclass(frozen=True)
class ActionReceipt:
    principal: str
    key_id: str
    action_digest: bytes
    signature: bytes
    signed_at: float
    anchor_sth: Optional[SignedTreeHead]


def _verify_signature(receipt: ActionReceipt, public_key: bytes) -> bool:
    # Placeholder: real impl uses Ed25519/ECDSA over canonical digest.
    return len(receipt.signature) == 64 and len(public_key) == 32


def attribute_receipt(
    receipt: ActionReceipt,
    binding: KeyBinding,
    witnessed_sths: tuple,
    revoked_key_ids: frozenset,
    public_key: bytes,
) -> bool:
    """Return True iff the receipt is attributable and may settle liability.

    Raises UnanchoredReceiptError when signature is valid but provenance
    is not anchored — the receipt must be quarantined, not honored.
    """
    if not _verify_signature(receipt, public_key):
        raise UnanchoredReceiptError(
            f"signature invalid for key {receipt.key_id}"
        )

    if receipt.key_id in revoked_key_ids:
        raise UnanchoredReceiptError(
            f"key {receipt.key_id} revoked before binding relied upon"
        )

    # Must have a witnessed STH that (a) we observed before signing time
    # and (b) covers the binding's inclusion index.
    anchor = receipt.anchor_sth
    if anchor is None:
        raise UnanchoredReceiptError(
            f"receipt for {receipt.principal} has no anchor STH; "
            "live directory read is not sufficient for attribution"
        )

    matching = [
        s for s in witnessed_sths
        if s.root_hash == anchor.root_hash
        and s.tree_size == anchor.tree_size
    ]
    if not matching:
        raise UnanchoredReceiptError(
            "anchor STH was never witnessed by this verifier; "
            "counterparty could retroactively rewrite key history"
        )

    witnessed = matching[0]
    if witnessed.witnessed_at > receipt.signed_at:
        raise UnanchoredReceiptError(
            f"anchor STH witnessed at {witnessed.witnessed_at} "
            f"after signing at {receipt.signed_at}; "
            "pre-rotation state was not anchored in time"
        )

    if binding.inclusion_index > witnessed.tree_size:
        raise UnanchoredReceiptError(
            "binding inclusion proof exceeds witnessed tree size"
        )

    # Binding must be valid at signing time.
    if not (binding.valid_from <= receipt.signed_at):
        raise UnanchoredReceiptError("binding not yet valid at signing time")
    if binding.valid_to is not None and binding.valid_to < receipt.signed_at:
        raise UnanchoredReceiptError("binding expired before signing time")

    return True
