# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-mints-a-delegated-capability-token-whose-scope-set-is-empty-after-per-tool-consent-negotiation.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-mints-a-delegated-capability-token-whose-scope-set-is-empty-after-per-tool-consent-negotiation.warrant.json
# Markdown SHA-256:   f8cf7b2a3875f96285f3ba6ea3894dbbaec6f31b6ad27105f72fcdd977b8a306
# Code AST SHA-256:   bdb4cf59d11375c3b723c28e6e3c9caa48fb7abb0cf8e64e830baf7373879c9f
# Verified Classes:   EmptyScopeMisclassifiedError, DelegatedCapabilityToken, Decision
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import FrozenSet


class EmptyScopeMisclassifiedError(RuntimeError):
    """Raised when an empty-scope DCT is treated as an authentication failure
    rather than an authenticated-but-unauthorized (consent-denied) request."""


@dataclass(frozen=True)
class DelegatedCapabilityToken:
    sub: str
    aud: str
    scope: FrozenSet[str]
    signature_valid: bool
    expired: bool


class Decision:
    ALLOW = "allow"
    FORBIDDEN = "forbidden"          # authenticated, unauthorized
    UNAUTHENTICATED = "unauthenticated"  # no valid credential


def authorize(token: DelegatedCapabilityToken | None, tool: str) -> str:
    # Branch 1: no valid credential at all.
    if token is None or not token.signature_valid or token.expired:
        return Decision.UNAUTHENTICATED

    # Branch 2: valid credential, empty scope. Must be classified as
    # consent-denied BEFORE any `if not token.scope` guard, which would
    # otherwise invert the classification into UNAUTHENTICATED.
    if len(token.scope) == 0:
        # Explicitly record the consent event; do not page on-call.
        _audit(token.sub, tool, reason="consent_denied_all")
        return Decision.FORBIDDEN

    # Branch 3: ordinary scope check.
    if tool not in token.scope:
        _audit(token.sub, tool, reason="consent_denied_tool")
        return Decision.FORBIDDEN

    return Decision.ALLOW


def mint(requested: FrozenSet[str], granted: FrozenSet[str], sub: str, aud: str) -> DelegatedCapabilityToken:
    # Reject the vacuous `all(...)` predicate: granted must be a subset,
    # but an empty granted set is a legal consent outcome, not a validation
    # failure. Guard against the classic bug where `all(s in requested for s
    # in granted)` passes vacuously and mints an unintended token.
    if not granted.issubset(requested):
        raise EmptyScopeMisclassifiedError(
            f"granted={granted} not subset of requested={requested}"
        )
    if len(granted) == 0:
        # Legal, but must be an explicit, audited consent decision.
        _audit(sub, tool="*", reason="consent_denied_all_at_mint")
    return DelegatedCapabilityToken(
        sub=sub, aud=aud, scope=granted, signature_valid=True, expired=False
    )


def _audit(sub: str, tool: str, reason: str) -> None:
    # Placeholder: emit to audit sink with reason tag, never as authn_failed.
    pass


def handle_request(token: DelegatedCapabilityToken | None, tool: str) -> str:
    decision = authorize(token, tool)
    if decision == Decision.UNAUTHENTICATED:
        # Route to anonymous handling; do NOT consult principal-keyed cache.
        return "401"
    if decision == Decision.FORBIDDEN:
        # Authenticated subject present; principal-keyed cache is unsafe to
        # serve here because it may hold broader-scope responses.
        raise EmptyScopeMisclassifiedError(
            f"principal={token.sub if token else None} tool={tool} "
            f"scope={sorted(token.scope) if token else None} -> 403 consent_denied"
        )
    return "200"
