"""Payment verification handlers for the Zarinpal REST v4 flow."""

import contextlib

from aiogram import F, Router
from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery

from database.db import (
    add_to_wallet,
    complete_payment,
    delete_payment,
    fail_payment,
    get_payment,
    update_order_status,
)
from keyboards.inline import back_to_menu_kb
from utils.delivery import deliver_product
from utils.zarinpal import verify_payment, amounts_match
from utils.emojis import get_pe, get_plain_emoji

router = Router(name="payment")


@router.callback_query(F.data == "pay:check")
async def cb_pay_check(callback: CallbackQuery, state: FSMContext) -> None:
    """Verify one persisted payment and deliver only after exact validation."""
    await callback.answer()
    try:
        state_data = await state.get_data()
        payment_id = state_data.get("payment_id")
        state_order_id = state_data.get("order_id")
        state_authority = state_data.get("authority")

        if not payment_id or not state_order_id or not state_authority:
            await callback.answer(f"{get_plain_emoji('warning')} پرداخت در انتظاری یافت نشد.", show_alert=True)
            return

        payment = await get_payment(payment_id)
        if not payment:
            await callback.answer(f"{get_plain_emoji('warning')} اطلاعات پرداخت یافت نشد.", show_alert=True)
            return

        # The gateway charge lives in the payment record (NOT the order —
        # a payment may be smaller than the order when the wallet covered
        # part of the price). Verify against the frozen payment amount and
        # never allow a gateway charge that exceeds the order total.
        gateway_amount_irt = int(payment["payment_amount_irt"])

        # Never trust mutable FSM values for the amount, authority, or order.
        if (
            payment["order_id"] != state_order_id
            or payment["authority"] != state_authority
            or payment["user_id"] != callback.from_user.id
            or payment["payment_status"] != "init"
            or payment["order_status"] != "pending"
            or gateway_amount_irt < 1
            or gateway_amount_irt > int(payment["order_amount_irt"])
        ):
            await fail_payment(payment_id)
            await callback.answer(
                f"{get_plain_emoji('cross')} اطلاعات پرداخت معتبر نیست. در صورت کسر از کیف پول، مبلغ بازگردانده شد.",
                show_alert=True,
            )
            return

        await callback.answer(f"{get_plain_emoji('hourglass')} در حال بررسی پرداخت…", show_alert=False)
        # Fetch the persisted integer amount from the DB — never recalculate.
        expected_amount = gateway_amount_irt
        result = await verify_payment(
            authority=payment["authority"],
            amount_irt=expected_amount,
        )

        # Compare the gateway's returned amount against the DB-stored int.
        # ``amounts_match`` also tolerates a 10x (Toman vs Rial) echo.
        # If Zarinpal did not return an amount, rely on the API code alone.
        if result.amount_irt is not None:
            amount_matches = amounts_match(expected_amount, int(result.amount_irt))
        else:
            amount_matches = True

        if not (
            result.success
            and result.code in (100, 101)
            and result.ref_id
            and amount_matches
        ):
            wallet_refunded = int(
                state_data.get("wallet_used") or payment["wallet_used"] or 0
            )
            await fail_payment(payment_id)

            refund_line = ""
            if wallet_refunded > 0:
                refund_str = f"{wallet_refunded:,}".replace(",", "،")
                refund_line = (
                    f"\n\n{get_pe('purse')} مبلغ <b>{refund_str} تومان</b> که از "
                    "کیف پول شما کسر شده بود، به‌صورت خودکار به کیف پول شما "
                    "بازگردانده شد."
                )
            with contextlib.suppress(TelegramBadRequest):
                await callback.message.edit_text(
                    f"{get_pe('cross')} <b>بررسی پرداخت ناموفق بود.</b>\n\n"
                    f"دلیل: {result.message or 'مبلغ پرداخت با سفارش مطابقت ندارد.'}"
                    f"{refund_line}\n\n"
                    "اگر فکر می‌کنید این خطا است، لطفاً دوباره تلاش کنید یا با پشتیبانی تماس بگیرید.",
                    reply_markup=back_to_menu_kb(),
                )
            return

        # This conditional transition prevents duplicate fulfillment.
        completed = await complete_payment(
            payment_id=payment_id,
            order_id=payment["order_id"],
            ref_id=result.ref_id,
        )
        if not completed:
            await callback.answer(f"{get_plain_emoji('check')} این پرداخت قبلاً بررسی شده است.", show_alert=True)
            return

        await deliver_product(
            bot=callback.bot,
            user_id=payment["user_id"],
            order_id=payment["order_id"],
            product=payment["product"],
            details=payment["details"] or "",
            extra={"country": state_data.get("country", "iran")},
        )
    finally:
        # Clear stale payment context on success, failure, or handler errors.
        await state.clear()


@router.callback_query(F.data == "pay:cancel")
async def cb_pay_cancel(callback: CallbackQuery, state: FSMContext) -> None:
    """Cancel the current payment, remove its record, and refund any wallet credit."""
    await callback.answer()
    try:
        data = await state.get_data()
        payment_id = data.get("payment_id")
        order_id = data.get("order_id")
        wallet_used = int(data.get("wallet_used") or 0)

        # Guard against cancelling an already-verified payment (which would
        # otherwise refund a wallet portion that was legitimately spent).
        if payment_id:
            payment = await get_payment(payment_id)
            if payment:
                if payment["payment_status"] == "verified":
                    with contextlib.suppress(TelegramBadRequest):
                        await callback.message.edit_text(
                            f"{get_pe('check')} این پرداخت قبلاً تأیید شده است.",
                            reply_markup=back_to_menu_kb(),
                        )
                    return
                # Prefer the persisted wallet portion (survives FSM resets).
                order_id = order_id or payment["order_id"]
                wallet_used = wallet_used or int(payment["wallet_used"] or 0)
            await delete_payment(payment_id)

        if order_id:
            await update_order_status(order_id, "cancelled")

        # If the checkout had deducted a wallet portion, give it back.
        if wallet_used > 0:
            await add_to_wallet(callback.from_user.id, wallet_used)

        with contextlib.suppress(TelegramBadRequest):
            await callback.message.edit_text(
                f"{get_pe('cross')} پرداخت لغو شد.\n\nبه منوی اصلی بازگردید:",
                reply_markup=back_to_menu_kb(),
            )
        await callback.answer()
    finally:
        await state.clear()
