"""Shared checkout rendering: invoice, payment methods, results.

Every product uses these helpers, so no flow re-implements payment screens.
"""
import logging
from typing import Any

from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, Message

from bot.keyboards.payment import (
    bank_keyboard,
    failed_keyboard,
    payment_methods_keyboard,
    success_keyboard,
    timeout_keyboard,
    tron_keyboard,
)
from bot.keyboards.purchase import confirm_keyboard
from bot.services.api import ApiError, api
from bot.services.texts import crypto, money, texts

logger = logging.getLogger("bot.checkout")

DETAIL_KEYS = ("username", "channel", "quantity", "amount", "wallet", "memo", "comment")


async def render(
    event: Message | CallbackQuery, text: str, keyboard: InlineKeyboardMarkup | None = None
) -> None:
    """Edit in place for button presses, send a new message otherwise."""
    if isinstance(event, CallbackQuery) and event.message:
        try:
            await event.message.edit_text(text, reply_markup=keyboard)
            return
        except TelegramBadRequest as exc:
            if "message is not modified" in str(exc):
                return
            await event.message.answer(text, reply_markup=keyboard)
            return
    target = event.message if isinstance(event, CallbackQuery) else event
    if target:
        await target.answer(text, reply_markup=keyboard)


async def invoice_text(order: dict[str, Any]) -> str:
    summary = order.get("summary") or {}
    lines: list[str] = []
    for key in DETAIL_KEYS:
        if summary.get(key) not in (None, ""):
            lines.append(await texts.text(f"invoice_detail_{key}", value=summary[key]))
    if "hide" in summary:
        lines.append(
            await texts.text("invoice_detail_hide_on" if summary["hide"] else "invoice_detail_hide_off")
        )

    return await texts.text(
        "invoice",
        title=order["product_snapshot"].get("title", ""),
        details="\n".join(lines) + ("\n" if lines else ""),
        price=money(order["price"]),
        discount=money(order["discount"]),
        final_price=money(order["final_price"]),
    )


async def show_invoice(
    event: Message | CallbackQuery, order: dict[str, Any], options: list[dict[str, Any]]
) -> None:
    keyboard = await confirm_keyboard(order["id"], options, order.get("input_data", {}))
    await render(event, await invoice_text(order), keyboard)


async def show_payment_methods(
    event: Message | CallbackQuery, order: dict[str, Any], methods: dict[str, Any]
) -> None:
    text = await texts.text(
        "payment_select", amount=money(methods["amount"]), balance=money(methods["balance"])
    )
    await render(event, text, await payment_methods_keyboard(order["id"], methods))


async def show_tron_invoice(
    event: Message | CallbackQuery, order: dict[str, Any], payment: dict[str, Any]
) -> None:
    minutes = await texts.setting("payment_timeout_minutes", 30)
    text = await texts.text(
        "tron_invoice",
        address=payment.get("wallet_address", ""),
        amount=crypto(payment.get("payable_amount")),
        order_number=order["order_number"],
        minutes=minutes,
    )
    await render(event, text, await tron_keyboard(payment["id"], order["id"]))


async def show_bank_invoice(
    event: Message | CallbackQuery, order: dict[str, Any], data: dict[str, Any]
) -> None:
    text = await texts.text(
        "bank_invoice",
        card_number=data.get("card_number", ""),
        card_owner=data.get("card_owner", ""),
        amount=money(order["final_price"]),
    )
    await render(event, text, await bank_keyboard(order["id"]))


async def show_result(event: Message | CallbackQuery, order: dict[str, Any]) -> None:
    """Final screen for any product: success, queued, waiting approval or failure."""
    status = order.get("status")
    snapshot = order.get("product_snapshot", {})

    if status == "completed":
        text = await texts.text(
            "order_success",
            order_number=order["order_number"],
            title=snapshot.get("title", ""),
            final_price=money(order["final_price"]),
        )
        await render(event, text, await success_keyboard(order["id"]))
        return

    if status in ("paid", "processing"):
        text = await texts.text("order_processing", order_number=order["order_number"])
        await render(event, text, await success_keyboard(order["id"]))
        return

    if order.get("payment_status") == "waiting_approval":
        from bot.keyboards.common import main_menu_keyboard

        await render(event, await texts.text("waiting_approval"), await main_menu_keyboard())
        return

    reason = order.get("error_message") or ""
    await render(
        event, await texts.text("order_failed", reason=reason), await failed_keyboard(order["id"])
    )


async def start_payment(
    event: CallbackQuery, state: FSMContext, telegram_id: int, order_id: str, method: str
) -> None:
    """One place where wallet / TRON / bank payments are kicked off."""
    try:
        result = await api.create_payment(telegram_id, order_id, method)
    except ApiError as exc:
        if isinstance(event, CallbackQuery):
            await event.answer(await texts.error(exc.code), show_alert=True)
        return

    order = result["order"]
    if method == "wallet":
        order = await _refresh(telegram_id, order_id, order)
        await show_result(event, order)
        await state.clear()
        return

    if method == "tron":
        await show_tron_invoice(event, order, result["payment"])
        await state.update_data(payment_id=result["payment"]["id"], order_id=order_id)
        return

    await show_bank_invoice(event, order, result)
    await state.update_data(payment_id=result["payment"]["id"], order_id=order_id)


async def _refresh(telegram_id: int, order_id: str, fallback: dict[str, Any]) -> dict[str, Any]:
    try:
        return await api.order(telegram_id, order_id)
    except ApiError:
        return fallback


async def show_timeout(event: CallbackQuery, order_id: str) -> None:
    await render(event, await texts.text("payment_timeout"), await timeout_keyboard(order_id))
