# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-presents-a-valid-aaif-credential-whose-did-document-has-been-rotated-mid-session.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-presents-a-valid-aaif-credential-whose-did-document-has-been-rotated-mid-session.warrant.json
# Markdown SHA-256:   c24c7bc8a03813f8309e55ebe9012eec22933a4e60aa9e38181558bc984e04a8
# Code AST SHA-256:   f7af8fe7216c7c8ad0675fd3d6912dcfd02dccdf18e57c5b8be160093b549608
# Verified Classes:   StaleAgentIdentityError, DIDDocument, AAIFCredential
# Defensive Guards:   5 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass

class StaleAgentIdentityError(RuntimeError):
    """Raised when an AAIF VC verifies cryptographically but the referenced
    verification method is no longer authorized in the current DID document."""
    pass

@dataclass
class DIDDocument:
    did: str
    updated: float                       # epoch seconds
    verification_methods: dict           # fragment -> key material
    authentication: set                  # fragments authorized for authn

@dataclass
class AAIFCredential:
    subject_did: str
    proof_method_fragment: str           # e.g. "#key-1"
    proof_purpose: str                   # "authentication"
    issuance_date: float
    exp: float
    signature_valid: bool                # result of crypto verify step

SKEW_ALLOWANCE = 300.0

def authorize_agent(cred: AAIFCredential, doc: DIDDocument, now: float | None = None) -> None:
    now = now if now is not None else time.time()

    if now >= cred.exp:
        raise StaleAgentIdentityError(
            f"credential for {cred.subject_did} expired at {cred.exp}"
        )
    if not cred.signature_valid:
        raise StaleAgentIdentityError(
            f"proof signature invalid for {cred.subject_did}"
        )
    if cred.proof_method_fragment not in doc.verification_methods:
        raise StaleAgentIdentityError(
            f"method {cred.proof_method_fragment} absent from current DID document "
            f"for {cred.subject_did}"
        )
    # The non-obvious check: presence is not authorization.
    if cred.proof_purpose == "authentication" and \
       cred.proof_method_fragment not in doc.authentication:
        raise StaleAgentIdentityError(
            f"method {cred.proof_method_fragment} present but deauthorized in "
            f"current DID document for {cred.subject_did} (rotated at {doc.updated})"
        )
    # Credential predates a rotation that touched the referenced method.
    if cred.issuance_date + SKEW_ALLOWANCE < doc.updated and \
       cred.proof_method_fragment not in doc.authentication:
        raise StaleAgentIdentityError(
            f"credential issuance {cred.issuance_date} precedes DID rotation "
            f"{doc.updated}; identity continuity not established"
        )
