# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-distinguish-forgotten-because-stale-from-forgotten-because-never-learned-in-a-read-write-forget-memory-tool-with-provenance-tra.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-distinguish-forgotten-because-stale-from-forgotten-because-never-learned-in-a-read-write-forget-memory-tool-with-provenance-tra.warrant.json
# Markdown SHA-256:   5ff7a70292b3a9bc2358f30ab19dac674854124c557baf3c993b40ba47aac823
# Code AST SHA-256:   c3424e7f80c4068e63d93754f7c95d2cc2024bc9745b1d2fe33259425007fc33
# Verified Classes:   MemoryState, ForgetReason, ResurrectionError, ReadResult
# Defensive Guards:   5 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from enum import Enum
from typing import Optional


class MemoryState(Enum):
    LIVE = "live"
    TOMBSTONED = "tombstoned"
    ABSENT = "absent"


class ForgetReason(Enum):
    SUPERSEDED = "superseded"
    RETRACTED = "retracted"
    CONTRADICTED = "contradicted"
    EXPIRED = "expired"


class ResurrectionError(RuntimeError):
    """Raised when a write would shadow a tombstone without an explicit resurrect."""


@dataclass(frozen=True)
class ReadResult:
    state: MemoryState
    record: Optional[dict] = None
    reason: Optional[ForgetReason] = None
    prior_confidence: Optional[float] = None
    prior_epoch: Optional[int] = None
    superseded_by: Optional[str] = None


def classify_absence(result: ReadResult) -> str:
    """Map a typed read result to an agent fallback policy. Raises on malformed tombstones."""
    if result.state is MemoryState.LIVE:
        return "use_record"
    if result.state is MemoryState.ABSENT:
        return "rederive_allowed"
    if result.state is MemoryState.TOMBSTONED:
        if result.reason is None or result.prior_confidence is None:
            raise ResurrectionError(
                f"tombstone missing reason/prior_confidence; cannot distinguish "
                f"stale from never-learned (prior_epoch={result.prior_epoch})"
            )
        if result.reason is ForgetReason.SUPERSEDED:
            if result.superseded_by is None:
                raise ResurrectionError(
                    "superseded tombstone has no superseded_by pointer; "
                    "re-derivation would resurrect a retired fact"
                )
            return f"follow_supersession:{result.superseded_by}"
        if result.reason in (ForgetReason.RETRACTED, ForgetReason.CONTRADICTED):
            return "rederive_independent_source_only"
        if result.reason is ForgetReason.EXPIRED:
            return "rederive_mark_as_expired_rederivation"
    raise ResurrectionError(f"unhandled read state: {result.state}")


def guarded_write(key: str, value: dict, store: dict, resurrect: bool = False,
                  justification: str = "") -> None:
    existing = store.get(key)
    if existing is not None and existing.state is MemoryState.TOMBSTONED:
        if not resurrect:
            raise ResurrectionError(
                f"write to {key!r} would shadow tombstone "
                f"(reason={existing.reason}, prior_confidence={existing.prior_confidence}); "
                f"pass resurrect=True with justification to override"
            )
        if not justification:
            raise ResurrectionError(
                f"resurrect of {key!r} requires a non-empty justification"
            )
    store[key] = ReadResult(state=MemoryState.LIVE, record=value)
