# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/why-does-an-instance-of-a2a-v1-0-agent-card-discovery-fail-signature-verification-after-a-registry-re-resolution-cycle-across-vendor-boundaries.md
# Remnant Warrant:    https://corpusfetch.com/c/why-does-an-instance-of-a2a-v1-0-agent-card-discovery-fail-signature-verification-after-a-registry-re-resolution-cycle-across-vendor-boundaries.warrant.json
# Markdown SHA-256:   a437730ed86aab7f014440d31d1b8b8639954d176bbf7806583dea1fed75358d
# Code AST SHA-256:   c390facd243a6ecc6f46b42f053c7bf08e8f78dfca5722edefb119280db95907
# Verified Classes:   CardRevisionMismatch, AgentCard
# Defensive Guards:   6 fail-closed invariants (zero external deps)

import hashlib
import json
from dataclasses import dataclass
from typing import Optional


class CardRevisionMismatch(RuntimeError):
    """Raised when the Agent Card revision needed to verify a delegation
    cannot be reconciled with the revision the registry currently serves."""


@dataclass(frozen=True)
class AgentCard:
    agent_id: str
    key_id: str
    security_schemes: dict
    revision: str  # content-addressed digest, e.g. sha256:...


def canonical_digest(card: AgentCard) -> str:
    body = json.dumps(
        {
            "agentId": card.agent_id,
            "keyId": card.key_id,
            "securitySchemes": card.security_schemes,
        },
        sort_keys=True,
        separators=(",", ":"),
    ).encode()
    return "sha256:" + hashlib.sha256(body).hexdigest()


def verify_delegation(
    *,
    header_card_digest: str,
    header_key_id: str,
    registry_resolved: Optional[AgentCard],
    pinned_cache: dict,  # digest -> AgentCard
    policy: str,  # "bind-to-digest" | "bind-to-current" | "bind-to-digest-with-cache-pin"
) -> AgentCard:
    """Resolve the card revision that authorizes a delegation envelope.

    Returns the card whose key material must be used for JWS verification.
    Raises CardRevisionMismatch when the policy cannot be satisfied.
    """
    if policy == "bind-to-digest-with-cache-pin":
        card = pinned_cache.get(header_card_digest)
        if card is None:
            raise CardRevisionMismatch(
                f"pinned cache miss for cardDigest={header_card_digest}; "
                f"delegation cannot be verified without the originating revision"
            )
        if card.key_id != header_key_id:
            raise CardRevisionMismatch(
                f"pinned card keyId={card.key_id} does not match header keyId={header_key_id}"
            )
        return card

    if registry_resolved is None:
        raise CardRevisionMismatch(
            "registry returned no card for agentId; cannot bind delegation"
        )

    resolved_digest = canonical_digest(registry_resolved)

    if policy == "bind-to-digest":
        if resolved_digest != header_card_digest:
            raise CardRevisionMismatch(
                f"registry revision {resolved_digest} != header cardDigest "
                f"{header_card_digest}; revision-addressed fetch required"
            )
        return registry_resolved

    if policy == "bind-to-current":
        if header_key_id not in {
            s.get("keyId") for s in registry_resolved.security_schemes.values()
            if isinstance(s, dict)
        } and registry_resolved.key_id != header_key_id:
            raise CardRevisionMismatch(
                f"header keyId={header_key_id} absent from current card "
                f"{resolved_digest}; delegation signed under superseded key"
            )
        return registry_resolved

    raise ValueError(f"unknown verification policy: {policy}")
