"""Provider adapters that revalidate weekly authority immediately before I/O."""

from __future__ import annotations

from collections.abc import Callable, Mapping
from typing import Never, Protocol, assert_never, final, override

from checkin_cli.weekly_operations_lifecycle import ReminderProviderUnknown

from .nutrition_weekly_operations import Topic59Transport
from .nutrition_weekly_operations_publication_contract import (
    Topic59ProviderDelivered,
    Topic59ProviderFailure,
    Topic59ProviderKnownFailure,
    Topic59ProviderOutcome,
    Topic59ProviderUnknown,
)
from .nutrition_weekly_owner_model import JsonValue, WeeklyExplanationModel
from .nutrition_weekly_reminder_authority import WeeklyOperationsTickSnapshot
from .nutrition_weekly_reminder import (
    TelegramReminderDelivered,
    TelegramReminderRejected,
)
from .telegram_edit_transport import (
    TelegramEditDelivered,
    TelegramEditKnownFailure,
    TelegramEditOutcome,

    TelegramEditUnknownReason,
)


class WeeklyOperationsHost(Protocol):
    async def send_weekly_reminder(
        self, chat_id: str, topic_id: str | None, text: str
    ) -> TelegramReminderDelivered | TelegramReminderRejected: ...

    async def edit_weekly_topic59_outcome(
        self, chat_id: str, message_id: str, text: str
    ) -> TelegramEditOutcome: ...

    def generate_weekly_explanation(
        self, request: Mapping[str, JsonValue]
    ) -> Mapping[str, JsonValue]: ...

    def weekly_operations_authority_current(
        self, snapshot: WeeklyOperationsTickSnapshot
    ) -> bool: ...


def _reminder_variant(
    value: TelegramReminderDelivered | TelegramReminderRejected,
) -> TelegramReminderDelivered | TelegramReminderRejected | str:
    return value


def _invalid_reminder_outcome(value: str) -> Never:
    raise AssertionError(f"invalid weekly reminder outcome: {value!r}")


@final
class RevalidatingTopic59Transport(Topic59Transport):
    def __init__(self, host: WeeklyOperationsHost, current: Callable[[], bool]) -> None:
        self._host = host
        self._current = current

    @override
    async def send(self, *, chat_id: str, topic_id: str, text: str) -> Topic59ProviderOutcome:
        if topic_id != "59" or not self._current():
            return Topic59ProviderKnownFailure(Topic59ProviderFailure.REJECTED)
        try:
            outcome = await self._host.send_weekly_reminder(chat_id, topic_id, text)
        except ReminderProviderUnknown:
            return Topic59ProviderUnknown(Topic59ProviderFailure.CONNECTION_LOST)
        match _reminder_variant(outcome):
            case TelegramReminderDelivered(message_id=message_id):
                return Topic59ProviderDelivered(message_id)
            case TelegramReminderRejected():
                return Topic59ProviderKnownFailure(Topic59ProviderFailure.REJECTED)
            case unreachable:
                assert_never(_invalid_reminder_outcome(unreachable))

    @override
    async def edit(
        self, *, chat_id: str, topic_id: str, message_id: str, text: str
    ) -> Topic59ProviderOutcome:
        if topic_id != "59" or not self._current():
            return Topic59ProviderKnownFailure(Topic59ProviderFailure.REJECTED)
        outcome = await self._host.edit_weekly_topic59_outcome(
            chat_id, message_id, text,
        )
        if isinstance(outcome, TelegramEditDelivered):
            return Topic59ProviderDelivered(outcome.message_id)
        if isinstance(outcome, TelegramEditKnownFailure):
            return Topic59ProviderKnownFailure(Topic59ProviderFailure.REJECTED)
        reason = (
            Topic59ProviderFailure.TIMEOUT
            if outcome.reason is TelegramEditUnknownReason.TIMEOUT
            else Topic59ProviderFailure.CONNECTION_LOST
        )
        return Topic59ProviderUnknown(reason)


@final
class RevalidatingWeeklyModel(WeeklyExplanationModel):
    def __init__(self, host: WeeklyOperationsHost, current: Callable[[], bool]) -> None:
        self._host = host
        self._current = current

    @override
    def generate(self, request: Mapping[str, JsonValue]) -> Mapping[str, JsonValue]:
        if not self._current():
            raise OSError("weekly authority changed before provider")
        return self._host.generate_weekly_explanation(request)
