"""Single-authority 20:00 weekly reminder provider lifecycle."""

from __future__ import annotations

from anyio import get_cancelled_exc_class

from collections.abc import Callable
from dataclasses import dataclass
from datetime import time
from enum import StrEnum
from typing import Final, Never, Protocol, assert_never
from zoneinfo import ZoneInfo

from .weekly_operations_schedule_host_models_r4 import (
    ScheduledDeliveryReceipt,
    WeeklyReminderReservationAuthority,
)
from .weekly_reminder_schedule import (
    abandon,
    abandon_answered,
    mark_audited,
    mark_delivered,
    mark_known_failure,
    mark_sending,
    mark_unknown,
    receipts,
    reserve,
)
from .weekly_operations import WeeklyOperationsConflict
from .weekly_operations_correlation import (
    CorrelationAction,
    CorrelationOutcome,
    CorrelationProjection,
    CorrelationProjectionInput,
    CorrelationScope,
    project_canonical_checkin,
)
from .weekly_reminder_authority import (
    BoundWeeklyReminderCustomer,
    WeeklyReminderRequest,
    reminder_template,
)

_REMINDER_TIME: Final = time(20)
_CUTOFF_TIME: Final = time(23)
_KST: Final = ZoneInfo("Asia/Seoul")


class ReminderLedgerState(StrEnum):
    PREPARED = "prepared"
    SENDING = "sending"
    DELIVERED = "delivered"
    SENT_AUDITED = "sent_audited"
    KNOWN_FAILURE = "known_failure"
    UNKNOWN = "unknown"
    ABANDONED = "abandoned"


class ReminderOutcome(StrEnum):
    NOT_DUE = "not_due"
    ANSWERED = "answered"
    SENT_AUDITED = "sent_audited"
    KNOWN_FAILURE = "known_failure"
    EXPLICIT_REJECTION = "explicit_rejection"
    AUTHORITY_INCIDENT = "authority_incident"
    UNKNOWN = "unknown"
    REPLAY = "replay"


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


@dataclass(frozen=True, slots=True)
class ProviderRejected:
    reason: str


class ReminderProviderUnknown(Exception):
    """The provider call may have produced a visible delivery."""


class ReminderProviderKnownFailure(Exception):
    """The provider proves that no delivery occurred."""


class ReminderProvider(Protocol):
    async def send(
        self, bound_customer: BoundWeeklyReminderCustomer
    ) -> ProviderDelivered | ProviderRejected: ...


@dataclass(frozen=True, slots=True)
class ReminderDependencies:
    provider: ReminderProvider
    resnapshot: Callable[[], BoundWeeklyReminderCustomer]


@dataclass(frozen=True, slots=True)
class ReminderResult:
    outcome: ReminderOutcome
    receipt: ScheduledDeliveryReceipt | None


def _local_time(request: WeeklyReminderRequest) -> time:
    now = request.now
    if now.tzinfo is None or now.utcoffset() is None:
        raise WeeklyOperationsConflict("weekly lifecycle time is naive")
    return now.astimezone(_KST).time().replace(tzinfo=None)


def _ledger_variant(value: str) -> ReminderLedgerState | str:
    try:
        return ReminderLedgerState(value)
    except ValueError:
        return value


def _provider_variant(
    value: ProviderDelivered | ProviderRejected,
) -> ProviderDelivered | ProviderRejected | str:
    return value


def _invalid_variant(value: str) -> Never:
    raise WeeklyOperationsConflict(f"unknown weekly lifecycle variant: {value}")


def _projection(
    bound: BoundWeeklyReminderCustomer,
    request: WeeklyReminderRequest,
    action: CorrelationAction,
) -> CorrelationProjection:
    with bound.source.read_locked() as snapshot:
        rows = bound.store.read()
        return project_canonical_checkin(
            CorrelationProjectionInput(
                CorrelationScope(bound.customer_identity_digest, request.kst_day, action),
                snapshot,
                rows,
            )
        )


def reminder_reservation_authority(
    bound: BoundWeeklyReminderCustomer,
) -> WeeklyReminderReservationAuthority:
    return WeeklyReminderReservationAuthority(
        bound.authority_digest,
        bound.customer_identity_digest,
        bound.candidate_digest,
        bound.canonical_registry_digest,
        bound.canonical_binding_digest,
        bound.sidecar_authority_digest,
        bound.sidecar_history_digest,
        bound.ledger.binding_digest,
        bound.owner_digest,
        bound.consent_digest,
        bound.feature_epoch,
        bound.route_digest,
        bound.canonical.sequence,
        bound.canonical.digest,
    )


def _existing(request: WeeklyReminderRequest) -> ScheduledDeliveryReceipt | None:
    identity = request.bound_customer.customer_identity_digest
    matches = tuple(
        receipt
        for receipt in receipts(request.bound_customer)
        if receipt.weekly_customer_identity_digest == identity
        and receipt.kst_day == request.kst_day
        and receipt.kind == "reminder"
    )
    if len(matches) > 1:
        raise WeeklyOperationsConflict("multiple weekly reminder reservation lineages")
    return matches[0] if matches else None


async def run_due_reminder(
    request: WeeklyReminderRequest,
    dependencies: ReminderDependencies,
) -> ReminderResult:
    """Run one weekly reminder from one sealed and re-snapshotted authority."""
    local = _local_time(request)
    if local < _REMINDER_TIME or local >= _CUTOFF_TIME:
        return ReminderResult(ReminderOutcome.NOT_DUE, None)
    initial = request.bound_customer
    async with initial.ledger.provider_admission():
        existing = _existing(request)
        if existing is not None:
            match _ledger_variant(existing.state):
                case ReminderLedgerState.SENDING:
                    unknown = mark_unknown(initial, existing, "provider_unknown_after_restart")
                    return ReminderResult(ReminderOutcome.UNKNOWN, unknown)
                case ReminderLedgerState.DELIVERED:
                    audited = mark_audited(initial, existing)
                    return ReminderResult(ReminderOutcome.SENT_AUDITED, audited)
                case ReminderLedgerState.SENT_AUDITED | ReminderLedgerState.ABANDONED:
                    return ReminderResult(ReminderOutcome.REPLAY, existing)
                case ReminderLedgerState.KNOWN_FAILURE:
                    return ReminderResult(ReminderOutcome.KNOWN_FAILURE, existing)
                case ReminderLedgerState.UNKNOWN:
                    return ReminderResult(ReminderOutcome.UNKNOWN, existing)
                case ReminderLedgerState.PREPARED:
                    prepared = existing
                case _ as unreachable:
                    assert_never(_invalid_variant(unreachable))
        else:
            projection = _projection(initial, request, CorrelationAction.CHECKIN)
            if projection.outcome not in {CorrelationOutcome.MISSING, CorrelationOutcome.AWAITING_CUTOFF}:
                return ReminderResult(ReminderOutcome.ANSWERED, None)
            authority = reminder_reservation_authority(initial)
            prepared = reserve(initial, request.kst_day, authority)
        try:
            current = dependencies.resnapshot()
        except WeeklyOperationsConflict:
            abandoned = abandon(initial, prepared, "weekly_authority_incident")
            return ReminderResult(ReminderOutcome.AUTHORITY_INCIDENT, abandoned)
        sending = mark_sending(initial, prepared, reminder_reservation_authority(current))
        if sending.state == "abandoned":
            return ReminderResult(ReminderOutcome.AUTHORITY_INCIDENT, sending)
        if not sending.provider_authority:
            return ReminderResult(ReminderOutcome.REPLAY, sending)
        if current.authority_digest != initial.authority_digest:
            raise WeeklyOperationsConflict("provider authority escaped final fence")
        projection = _projection(current, request, CorrelationAction.CHECKIN)
        if projection.outcome not in {CorrelationOutcome.MISSING, CorrelationOutcome.AWAITING_CUTOFF}:
            abandoned = abandon_answered(initial, sending)
            return ReminderResult(ReminderOutcome.ANSWERED, abandoned)
        current.ledger.verify()
        current.route.verify()
        _ = reminder_template(current)
        try:
            provider_result = await dependencies.provider.send(current)
        except get_cancelled_exc_class():
            _ = mark_unknown(initial, sending, "provider_interrupted")
            raise
        except ReminderProviderKnownFailure as error:
            failed = mark_known_failure(initial, sending, str(error) or "provider_known_failure")
            return ReminderResult(ReminderOutcome.KNOWN_FAILURE, failed)
        except ReminderProviderUnknown:
            unknown = mark_unknown(initial, sending, "provider_unknown")
            return ReminderResult(ReminderOutcome.UNKNOWN, unknown)
        match _provider_variant(provider_result):
            case ProviderRejected(reason=reason):
                failed = mark_known_failure(initial, sending, reason)
                return ReminderResult(ReminderOutcome.EXPLICIT_REJECTION, failed)
            case ProviderDelivered(provider_receipt=provider_receipt, message_id=message_id):
                delivered = mark_delivered(initial, sending, provider_receipt, message_id)
                audited = mark_audited(initial, delivered)
                return ReminderResult(ReminderOutcome.SENT_AUDITED, audited)
            case _ as unreachable:
                assert_never(_invalid_variant(unreachable))
