# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-successful-scoped-forget-as-a-tombstone-rather-than-an-absence.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-successful-scoped-forget-as-a-tombstone-rather-than-an-absence.warrant.json
# Markdown SHA-256:   aadd89e7dfcd9b72ad7b2ee13d3b6dfa9c35c9b2ea54e1640c5f23afdd5fc1eb
# Code AST SHA-256:   b3ffce3347b066c61ef211b4ceecdf5d6da0d7514baf5dc944a11dd3907e4211
# Verified Classes:   ForgetScope, ScopedForgetViolation, Scope, Tombstone, MemoryStore
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

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


class ForgetScope(str, Enum):
    TENANT = "tenant"
    SESSION = "session"
    KEY = "key"


class ScopedForgetViolation(RuntimeError):
    """Raised when a write would resurrect a key forgotten at an intersecting scope."""


@dataclass(frozen=True)
class Scope:
    tenant: str
    session: Optional[str] = None
    key: Optional[str] = None

    def intersects(self, other: "Scope") -> bool:
        if self.tenant != other.tenant:
            return False
        if self.session is not None and other.session is not None:
            if self.session != other.session:
                return False
        if self.key is not None and other.key is not None:
            if self.key != other.key:
                return False
        return True

    def contains(self, other: "Scope") -> bool:
        """True if self is a superset of other (self is wider or equal)."""
        if self.tenant != other.tenant:
            return False
        if self.session is not None and self.session != other.session:
            return False
        if self.key is not None and self.key != other.key:
            return False
        return True


@dataclass
class Tombstone:
    scope: Scope
    forgotten_at: float


class MemoryStore:
    def __init__(self) -> None:
        self._records: dict[Scope, bytes] = {}
        self._tombstones: list[Tombstone] = []

    def forget(self, scope: Scope, now: float) -> int:
        removed = 0
        for s in list(self._records):
            if scope.contains(s):
                del self._records[s]
                removed += 1
        self._tombstones.append(Tombstone(scope=scope, forgotten_at=now))
        return removed

    def read(self, scope: Scope) -> Optional[bytes]:
        # Tombstone check precedes record lookup: a forgotten scope reads as
        # tombstone, not as absence, for any reader whose scope intersects it.
        for t in self._tombstones:
            if t.scope.intersects(scope) and t.scope.contains(scope):
                return None  # tombstoned
        return self._records.get(scope)

    def is_tombstoned(self, scope: Scope) -> bool:
        return any(t.scope.contains(scope) for t in self._tombstones)

    def write(self, scope: Scope, value: bytes, now: float) -> None:
        # The boundary invariant: a write must be refused if any tombstone at an
        # intersecting scope would be shadowed by this write. Absence is NOT
        # sufficient evidence that the key is writable.
        for t in self._tombstones:
            if t.scope.intersects(scope):
                raise ScopedForgetViolation(
                    f"write at {scope} intersects tombstone at {t.scope} "
                    f"(forgotten_at={t.forgotten_at}); refusing resurrection"
                )
        self._records[scope] = value


def safe_write(
    store: MemoryStore,
    scope: Scope,
    value: bytes,
    now: float,
    *,
    allow_disjoint_scope: bool = True,
) -> None:
    if store.is_tombstoned(scope):
        if not allow_disjoint_scope:
            raise ScopedForgetViolation(
                f"scope {scope} is tombstoned; writes disabled by policy"
            )
        raise ScopedForgetViolation(
            f"scope {scope} is tombstoned; re-write requires a strictly "
            f"disjoint scope, which this call cannot prove"
        )
    store.write(scope, value, now)
