"""Crash classification and append-only recovery decisions."""

from __future__ import annotations

from dataclasses import dataclass
from enum import StrEnum
from typing import assert_never


class RecoveryError(RuntimeError):
    """Durable target bytes are divergent and the attempt is consumed."""


class TargetState(StrEnum):
    """Exhaustive durable target classification."""

    EMPTY = "empty"
    STRICT_PREFIX = "strict_prefix"
    COMPLETE = "complete"
    DIVERGENT = "divergent"


@dataclass(frozen=True, slots=True)
class RecoveryDecision:
    """The only permitted continuation for observed durable bytes."""

    state: TargetState
    append_suffix: bytes
    roll_forward: bool


def classify_target(current: bytes, expected: bytes, *, intent_durable: bool) -> RecoveryDecision:
    """Classify bytes without authorizing rollback after any row byte exists."""
    if not current:
        state = TargetState.EMPTY
    elif current == expected:
        state = TargetState.COMPLETE
    elif expected.startswith(current):
        state = TargetState.STRICT_PREFIX
    else:
        state = TargetState.DIVERGENT
    match state:
        case TargetState.EMPTY:
            return RecoveryDecision(state, expected if intent_durable else b"", intent_durable)
        case TargetState.STRICT_PREFIX:
            return RecoveryDecision(state, expected[len(current) :], True)
        case TargetState.COMPLETE:
            return RecoveryDecision(state, b"", True)
        case TargetState.DIVERGENT:
            raise RecoveryError("divergent, extra, or unrecognized target bytes")
        case unreachable:
            assert_never(unreachable)
