"""Typed terminal states and provider outcomes for Topic-59 publication."""

from __future__ import annotations

from dataclasses import dataclass
from enum import StrEnum
from typing import TYPE_CHECKING, Literal

if TYPE_CHECKING:
    from .nutrition_weekly_operations_models import Topic59Projection


class Topic59LedgerError(Exception):
    """The Topic-59 publication history cannot safely advance."""


class Topic59LedgerCorruption(Topic59LedgerError):
    """Stored publication bytes failed their typed integrity contract."""


class Topic59LedgerConflict(Topic59LedgerError):
    """A reserved publication no longer has a reconcilable pending state."""


class Topic59LedgerState(StrEnum):
    PREPARED = "prepared"
    SENDING = "sending"
    DELIVERED = "delivered"
    SENT_AUDITED = "sent_audited"
    UNKNOWN = "unknown"
    FAILED = "failed"
    AUTHORITY_MISMATCH = "authority_mismatch"


class Topic59ProviderFailure(StrEnum):
    REJECTED = "rejected"
    TIMEOUT = "timeout"
    CONNECTION_LOST = "connection_lost"
    RECEIPT_MISMATCH = "receipt_mismatch"


class Topic59IncidentReason(StrEnum):
    AUTHORITY_REJECTED = "authority_rejected"
    AUTHORITY_MISMATCH = "authority_mismatch"
    LEDGER_CORRUPTION = "ledger_corruption"
    MISSING_MESSAGE_ID = "missing_message_id"
    UNRESOLVED_RESERVATION = "unresolved_reservation"
    PROVIDER_TERMINAL = "provider_terminal"


@dataclass(frozen=True, slots=True)
class Topic59ProviderDelivered:
    message_id: str

    def __post_init__(self) -> None:
        if not self.message_id.isdecimal() or len(self.message_id) > 19:
            raise Topic59LedgerError("provider receipt")


@dataclass(frozen=True, slots=True)
class Topic59ProviderKnownFailure:
    reason: Topic59ProviderFailure


@dataclass(frozen=True, slots=True)
class Topic59ProviderUnknown:
    reason: Topic59ProviderFailure


Topic59ProviderOutcome = Topic59ProviderDelivered | Topic59ProviderKnownFailure | Topic59ProviderUnknown


@dataclass(frozen=True, slots=True)
class Topic59LedgerAction:
    kind: Literal["send", "edit", "noop", "incident"]
    projection: Topic59Projection
    operation: Literal["send", "edit"] | None = None
    message_id: str | None = None
    reason: Topic59IncidentReason | None = None
