"""Weekly owner-card rendering and callback controls."""

from __future__ import annotations

from dataclasses import dataclass
from enum import StrEnum
import re
from typing import TYPE_CHECKING, Never, assert_never

from gateway.platforms.nutrition_draft_callback_route import (
    NutritionDraftCallbackDenied,
    NutritionDraftCallbackDenialReason,
    nutrition_draft_callback_route_denial,
)

if TYPE_CHECKING:
    from telegram import InlineKeyboardMarkup, Message

    from gateway.platforms.nutrition_coaching import DraftAction, IncomingAddress


@dataclass(frozen=True, slots=True)
class WeeklyOwnerEditControl:
    acknowledgement: str
    card_text: str


def weekly_owner_card(action: DraftAction) -> str | None:
    from gateway.platforms.nutrition_weekly_owner_card import (
        render_weekly_owner_card,
    )

    return render_weekly_owner_card(action)


def weekly_owner_callback_denial(
    actual: IncomingAddress,
    configured: IncomingAddress | None,
) -> NutritionDraftCallbackDenied | None:
    return nutrition_draft_callback_route_denial(
        actual.key,
        None if configured is None else configured.key,
    )


def weekly_owner_route_unavailable() -> NutritionDraftCallbackDenied:
    return NutritionDraftCallbackDenied(
        NutritionDraftCallbackDenialReason.ROUTE_UNAVAILABLE,
    )


def weekly_owner_edit_control(
    action: DraftAction,
    legacy_card_text: str,
) -> WeeklyOwnerEditControl:
    card = weekly_owner_card(action)
    if card is not None:
        return WeeklyOwnerEditControl(
            "수정할 고객 문구를 보내주세요.",
            f"{card}\n수정 안내: 고객 문구를 보내세요.",
        )
    instruction = (
        "문구만 수정하려면 새 문구를 그대로 보내세요.\n"
        "목표도 수정하려면 첫 줄에 `목표 kcal 단백질g 탄수화물g 지방g`, "
        "다음 줄부터 고객 문구를 보내세요."
    )
    return WeeklyOwnerEditControl(
        "수정할 목표와 고객 문구를 이 토픽으로 보내주세요.",
        f"{legacy_card_text}\n\n{instruction}",
    )


def weekly_owner_invalid_edit_text(action: DraftAction) -> str:
    if weekly_owner_card(action) is not None:
        return "수정 안내: 고객 문구를 입력해 주세요."
    return (
        "수정 형식을 확인해 주세요. 목표 수정은 첫 줄에 "
        "`목표 kcal 단백질g 탄수화물g 지방g`, "
        "다음 줄에 고객 문구를 입력합니다."
    )


class _OwnerControlStatus(StrEnum):
    GENERATION_FAILED = "generation_failed"
    CREATED = "created"
    EDITED = "edited"
    APPROVED = "approved"
    OTHER = "other"


def _owner_control_status(value: str) -> _OwnerControlStatus | str:
    try:
        return _OwnerControlStatus(value)
    except ValueError:
        return _OwnerControlStatus.OTHER


def _invalid_owner_control_status(value: str) -> Never:
    raise AssertionError(f"invalid owner control status: {value!r}")


def weekly_owner_markup(
    action: DraftAction, render_identity: str | None, card_nonce: str | None,
) -> InlineKeyboardMarkup | None:
    from telegram import InlineKeyboardButton, InlineKeyboardMarkup

    draft_id = action.draft_id or ""
    if (
        not draft_id
        or card_nonce is None
        or not isinstance(render_identity, str)
        or re.fullmatch(r"[a-f0-9]{1,16}", render_identity) is None
    ):
        return None

    def callback(operation: str) -> str:
        return f"n3:{draft_id}:{operation}:{card_nonce}:{render_identity}"

    match _owner_control_status(action.status):
        case _OwnerControlStatus.GENERATION_FAILED:
            rows = [[InlineKeyboardButton("재시도", callback_data=callback("r"))]]
        case _OwnerControlStatus.CREATED | _OwnerControlStatus.EDITED:
            rows = [
                [
                    InlineKeyboardButton("수정", callback_data=callback("e")),
                    InlineKeyboardButton("재생성", callback_data=callback("r")),
                ],
                [
                    InlineKeyboardButton("승인", callback_data=callback("a")),
                    InlineKeyboardButton("보류", callback_data=callback("h")),
                ],
            ]
        case _OwnerControlStatus.APPROVED:
            rows = [[
                InlineKeyboardButton("고객에게 보내기", callback_data=callback("s")),
                InlineKeyboardButton("승인 철회", callback_data=callback("v")),
            ]]
        case _OwnerControlStatus.OTHER:
            rows = []
        case unreachable:
            assert_never(_invalid_owner_control_status(unreachable))
    if any(
        len(str(item.callback_data).encode("utf-8")) > 64
        for row in rows
        for item in row
    ):
        return None
    return InlineKeyboardMarkup(rows) if rows else None


def weekly_owner_reconcile_markup(
    action: DraftAction, render_identity: str | None, card_nonce: str | None,
) -> InlineKeyboardMarkup | None:
    from telegram import InlineKeyboardButton, InlineKeyboardMarkup

    draft_id = action.draft_id
    if (
        draft_id is None
        or card_nonce is None
        or not isinstance(render_identity, str)
        or re.fullmatch(r"[a-f0-9]{1,16}", render_identity) is None
    ):
        return None
    callback_data = f"n3:{draft_id}:c:{card_nonce}:{render_identity}"
    if len(callback_data.encode("utf-8")) > 64:
        return None
    return InlineKeyboardMarkup([[
        InlineKeyboardButton("전달 기록 복구", callback_data=callback_data),
    ]])


def telegram_thread_context(message: Message) -> tuple[int | None, bool]:
    observed = message.message_thread_id
    thread_id = (
        observed
        if isinstance(observed, int) and not isinstance(observed, bool)
        else None
    )
    return thread_id, getattr(message, "is_topic_message", False) is True


def real_telegram_thread_id(value: int | bool | None) -> int | None:
    return value if type(value) is int else None


class TelegramWeeklyOwnerMixin:
    """Expose owner controls without Telegram SDK imports."""

    @staticmethod
    def _nutrition_generation_card_nonce(action: DraftAction) -> str | None:
        _ = action
        raise NotImplementedError

    @classmethod
    def _nutrition_draft_markup(
        cls, action: DraftAction, *, render_identity: str | None = None,
    ) -> InlineKeyboardMarkup | None:
        return weekly_owner_markup(
            action, render_identity, cls._nutrition_generation_card_nonce(action),
        )

    @classmethod
    def _nutrition_draft_reconcile_markup(
        cls, action: DraftAction, *, render_identity: str | None = None,
    ) -> InlineKeyboardMarkup | None:
        return weekly_owner_reconcile_markup(
            action, render_identity, cls._nutrition_generation_card_nonce(action),
        )

    @staticmethod
    def _weekly_owner_callback_denial(
        actual: IncomingAddress, configured: IncomingAddress | None,
    ) -> NutritionDraftCallbackDenied | None:
        return weekly_owner_callback_denial(actual, configured)

    @staticmethod
    def _weekly_owner_card(action: DraftAction) -> str | None:
        return weekly_owner_card(action)

    @staticmethod
    def _weekly_owner_edit_control(
        action: DraftAction, legacy_card_text: str,
    ) -> WeeklyOwnerEditControl:
        return weekly_owner_edit_control(action, legacy_card_text)

    @staticmethod
    def _weekly_owner_invalid_edit_text(action: DraftAction) -> str:
        return weekly_owner_invalid_edit_text(action)

    @staticmethod
    def _weekly_owner_route_unavailable() -> NutritionDraftCallbackDenied:
        return weekly_owner_route_unavailable()

    @staticmethod
    def _telegram_thread_context(message: Message) -> tuple[int | None, bool]:
        return telegram_thread_context(message)

    @staticmethod
    def _real_telegram_thread_id(value: int | bool | None) -> int | None:
        return real_telegram_thread_id(value)

    @staticmethod
    def _weekly_owner_markup(
        action: DraftAction, render_identity: str | None, card_nonce: str | None,
    ) -> InlineKeyboardMarkup | None:
        return weekly_owner_markup(action, render_identity, card_nonce)

    @staticmethod
    def _weekly_owner_reconcile_markup(
        action: DraftAction, render_identity: str | None, card_nonce: str | None,
    ) -> InlineKeyboardMarkup | None:
        return weekly_owner_reconcile_markup(action, render_identity, card_nonce)
