# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-silent-corruption-when-self-editing-context-windows-are-rolled-back-across-a-partial-in-place-rewrite.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-silent-corruption-when-self-editing-context-windows-are-rolled-back-across-a-partial-in-place-rewrite.warrant.json
# Markdown SHA-256:   fca0868014dccd5601d1875c7a1686685d88a5779549861ccd490be06a003b6e
# Code AST SHA-256:   01430bb4ef5474a4edbc6a2b97b20fef1192223140ab98d7022992ea7a106e8f
# Verified Classes:   RollbackIntegrityError, Inverse, RewriteBuffer
# Defensive Guards:   3 fail-closed invariants (zero external deps)

import hashlib
from dataclasses import dataclass


class RollbackIntegrityError(RuntimeError):
    """Raised when the undo log cannot guarantee a sound inverse of buffer state."""
    pass


@dataclass
class Inverse:
    anchor_before: str   # hash of N bytes preceding the edit
    anchor_after: str    # hash of N bytes following the edit
    original_bytes: bytes
    inserted_len: int


class RewriteBuffer:
    ANCHOR = 16

    def __init__(self, data: bytes):
        self.buf = bytearray(data)
        self.log: list[Inverse] = []

    def _anchor(self, start: int, end: int) -> str:
        return hashlib.sha256(bytes(self.buf[start:end])).hexdigest()

    def splice(self, offset: int, delete_len: int, insert: bytes) -> None:
        # Write-ahead: build and COMMIT the inverse BEFORE mutating the buffer.
        before = self._anchor(max(0, offset - self.ANCHOR), offset)
        after = self._anchor(offset + delete_len,
                             offset + delete_len + self.ANCHOR)
        inv = Inverse(before, after, bytes(self.buf[offset:offset + delete_len]),
                      len(insert))
        # Simulate a durable append that may fail; if it fails, we must NOT mutate.
        if not self._append_durable(inv):
            raise RollbackIntegrityError(
                "inverse record not committed; refusing in-place rewrite "
                "(buffer must not outrun its undo log)"
            )
        self.buf[offset:offset + delete_len] = insert

    def _append_durable(self, inv: Inverse) -> bool:
        # Placeholder for real journal commit; here always succeeds.
        self.log.append(inv)
        return True

    def rollback(self, steps: int = 1) -> None:
        for _ in range(steps):
            if not self.log:
                raise RollbackIntegrityError("rollback past start of log")
            inv = self.log[-1]
            # Locate the edit by content anchors, not stale absolute offsets.
            pos = self._find_by_anchors(inv)
            if pos is None:
                raise RollbackIntegrityError(
                    "anchor mismatch: buffer diverged from undo log, "
                    "rollback would corrupt context"
                )
            self.buf[pos:pos + inv.inserted_len] = inv.original_bytes
            self.log.pop()

    def _find_by_anchors(self, inv: Inverse):
        n = len(self.buf)
        for pos in range(n + 1):
            b = self._anchor(max(0, pos - self.ANCHOR), pos)
            if b != inv.anchor_before:
                continue
            end = pos + inv.inserted_len
            a = self._anchor(end, end + self.ANCHOR)
            if a == inv.anchor_after:
                return pos
        return None
