# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-treat-an-mcp-sse-session-resumption-as-a-new-logical-stream-rather-than-a-continuation-the-last-event-id-epoch-collision-boundar.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-treat-an-mcp-sse-session-resumption-as-a-new-logical-stream-rather-than-a-continuation-the-last-event-id-epoch-collision-boundar.warrant.json
# Markdown SHA-256:   44736700da456475d0136f0b208d1aa9c3a4e2fff9ee855e3b47d316554adeaf
# Code AST SHA-256:   8cc4a349e565f1565a32dd9f4683febff44cc7af2808ef43ad24ce8dd1a08b65
# Verified Classes:   ResumptionEpochMismatch, ResumptionGap, EventId, SseSession
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

import uuid
from dataclasses import dataclass


class ResumptionEpochMismatch(RuntimeError):
    """Raised when a Last-Event-ID cursor belongs to a foreign server epoch."""


class ResumptionGap(RuntimeError):
    """Raised when the cursor is same-epoch but the requested seq is no longer buffered."""


@dataclass(frozen=True)
class EventId:
    epoch: str
    seq: int

    def encode(self) -> str:
        return f"{self.epoch}:{self.seq}"

    @classmethod
    def decode(cls, raw: str) -> "EventId":
        epoch, _, seq = raw.rpartition(":")
        if not epoch or not seq.isdigit():
            raise ValueError(f"malformed event id: {raw!r}")
        return cls(epoch=epoch, seq=int(seq))


class SseSession:
    def __init__(self) -> None:
        # A fresh epoch per process start. Never reused, never derived from seq.
        self.epoch = uuid.uuid4().hex
        self._buffer: dict[int, str] = {}
        self._next_seq = 1

    def emit(self, payload: str) -> EventId:
        eid = EventId(epoch=self.epoch, seq=self._next_seq)
        self._buffer[eid.seq] = payload
        self._next_seq += 1
        return eid

    def resume(self, last_event_id: str) -> list[tuple[EventId, str]]:
        cursor = EventId.decode(last_event_id)
        if cursor.epoch != self.epoch:
            # Cross-epoch: the cursor is from a foreign incarnation.
            # Do NOT clamp, replay, or otherwise "best effort" continue.
            raise ResumptionEpochMismatch(
                f"cursor epoch {cursor.epoch!r} != server epoch {self.epoch!r}; "
                "client must re-initialize the logical session"
            )
        start = cursor.seq + 1
        if start < self._next_seq and start not in self._buffer:
            # Same epoch but the ring buffer has evicted the continuation.
            raise ResumptionGap(
                f"event {start} evicted; buffer holds {min(self._buffer, default='-')}"
                f"..{max(self._buffer, default='-')}"
            )
        return [
            (EventId(self.epoch, s), self._buffer[s])
            for s in range(start, self._next_seq)
            if s in self._buffer
        ]
