# app/src/telegram/ui/exchange_error.py

from __future__ import annotations

from dataclasses import dataclass

from aiogram.exceptions import TelegramBadRequest
from aiogram.types import (
    CallbackQuery,
    InaccessibleMessage,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    Message,
)

from src.integrations.exchange.runtime_ui import build_exchange_error_ui_parts
from src.telegram.ui.common import mode_line, now_line


@dataclass(slots=True)
class ExchangeErrorView:
    headline: str
    details: str


DEFAULT_NETWORK_DETAILS = "Не удалось получить данные с биржи.\nОбнови экран."
DEFAULT_AUTH_DETAILS = (
    "Не удалось выполнить приватный запрос к аккаунту.\n"
    "Проверь API-ключ, Secret Key, IP whitelist и права доступа."
)
DEFAULT_TIME_DETAILS = (
    "Не удалось выполнить запрос к бирже.\n"
    "Проверь синхронизацию времени и обнови экран."
)
DEFAULT_GENERIC_DETAILS = "Не удалось получить данные с биржи.\nОбнови экран."


# Собрать UI-представление ошибки через единый exchange status layer.
def _build_exchange_error_view(
    *,
    exc: Exception,
    network_details: str,
    auth_details: str,
    time_details: str | None = None,
    generic_details: str | None = None,
) -> ExchangeErrorView:
    _, headline, details = build_exchange_error_ui_parts(exc)

    return ExchangeErrorView(
        headline=headline,
        details=details or generic_details or DEFAULT_GENERIC_DETAILS,
    )


# Отрисовать единый текст ошибки биржи для message/callback экранов.
def render_exchange_error(
    *,
    title: str,
    exc: Exception,
    network_details: str = DEFAULT_NETWORK_DETAILS,
    auth_details: str = DEFAULT_AUTH_DETAILS,
    time_details: str | None = None,
    generic_details: str | None = None,
) -> str:
    view = _build_exchange_error_view(
        exc=exc,
        network_details=network_details,
        auth_details=auth_details,
        time_details=time_details,
        generic_details=generic_details,
    )

    return (
        f"{title}\n"
        f"{mode_line()}"
        f"{view.headline}\n\n"
        f"{view.details}\n\n"
        f"{now_line()}"
    )


# Собрать клавиатуру для экранов с ошибкой биржи.
def exchange_error_keyboard(
    *,
    retry_callback_data: str | None = None,
    back_callback_data: str | None = None,
    drafts_page: int | None = None,
) -> InlineKeyboardMarkup:
    buttons: list[list[InlineKeyboardButton]] = []
    first_row: list[InlineKeyboardButton] = []

    if retry_callback_data:
        first_row.append(
            InlineKeyboardButton(
                text="🔁 Обновить",
                callback_data=retry_callback_data,
            )
        )

    if back_callback_data:
        first_row.append(
            InlineKeyboardButton(
                text="⬅️ Назад",
                callback_data=back_callback_data,
            )
        )

    if first_row:
        buttons.append(first_row)

    if drafts_page is not None:
        buttons.append(
            [
                InlineKeyboardButton(
                    text="📚 К черновикам",
                    callback_data=f"drafts:{drafts_page}",
                )
            ]
        )
    elif back_callback_data is None:
        buttons.append(
            [
                InlineKeyboardButton(
                    text="🤖 К автоторговле",
                    callback_data="auto:home",
                )
            ]
        )

    return InlineKeyboardMarkup(inline_keyboard=buttons)


# Показать ошибку биржи через редактирование callback-сообщения.
async def show_callback_exchange_error(
    callback: CallbackQuery,
    *,
    title: str,
    exc: Exception,
    network_details: str = DEFAULT_NETWORK_DETAILS,
    auth_details: str = DEFAULT_AUTH_DETAILS,
    time_details: str | None = None,
    generic_details: str | None = None,
    retry_callback_data: str | None = None,
    back_callback_data: str | None = None,
    drafts_page: int | None = None,
) -> None:
    message = callback.message

    if message is None or isinstance(message, InaccessibleMessage):
        await callback.answer("Сообщение не найдено", show_alert=True)
        return

    text = render_exchange_error(
        title=title,
        exc=exc,
        network_details=network_details,
        auth_details=auth_details,
        time_details=time_details,
        generic_details=generic_details,
    )

    markup = exchange_error_keyboard(
        retry_callback_data=retry_callback_data or callback.data,
        back_callback_data=back_callback_data,
        drafts_page=drafts_page,
    )

    try:
        await message.edit_text(text, reply_markup=markup)
        await callback.answer()
    except TelegramBadRequest as tg_exc:
        if "message is not modified" in str(tg_exc).lower():
            await callback.answer("Ошибка всё ещё актуальна")
            return

        raise


# Показать ошибку биржи новым сообщением.
async def show_message_exchange_error(
    message: Message,
    *,
    title: str,
    exc: Exception,
    network_details: str = DEFAULT_NETWORK_DETAILS,
    auth_details: str = DEFAULT_AUTH_DETAILS,
    time_details: str | None = None,
    generic_details: str | None = None,
    retry_callback_data: str | None = None,
    back_callback_data: str | None = None,
    drafts_page: int | None = None,
) -> None:
    await message.answer(
        render_exchange_error(
            title=title,
            exc=exc,
            network_details=network_details,
            auth_details=auth_details,
            time_details=time_details,
            generic_details=generic_details,
        ),
        reply_markup=exchange_error_keyboard(
            retry_callback_data=retry_callback_data,
            back_callback_data=back_callback_data,
            drafts_page=drafts_page,
        ),
    )