"""
Shared product delivery logic.

Called by:
  • handlers/payment.py   (manual "پرداخت کردم" button flow)
  • bot.py /verify         (automatic Zarinpal callback flow)

Both paths converge here so the delivery logic is never duplicated.
All user-facing text in Persian (فارسی).
"""

import asyncio
import contextlib
import logging
import random
from typing import Optional

from aiogram import Bot
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton

from config import config
from database.db import get_order, update_order_status
from utils.emojis import get_pe, get_plain_emoji

logger = logging.getLogger(__name__)


# ─── Public API ──────────────────────────────────────────────────────

async def deliver_product(
    bot: Bot,
    user_id: int,
    order_id: int,
    product: str,
    details: str = "",
    extra: Optional[dict] = None,
) -> None:
    """Deliver the purchased product to *user_id*."""
    extra = extra or {}

    try:
        if "شارژ کیف پول" in product:
            await _deliver_wallet_recharge(bot, user_id, order_id, product, details)
        elif "شماره مجازی" in product or "virtual" in product.lower():
            await _deliver_virtual_number(bot, user_id, order_id, product, details)
        elif "ChatGPT" in product or "Gemini" in product:
            await _deliver_ai_account(bot, user_id, order_id, product)
        elif "طراحی" in product or "Design" in product:
            await _deliver_design(bot, user_id, order_id, product, details)
        else:
            await _deliver_generic(bot, user_id, order_id, product)
    except Exception as exc:
        logger.exception("Delivery failed for order #%s: %s", order_id, exc)
        await _safe_send(
            bot,
            user_id,
            f"{get_pe('warning')} خطایی در تحویل خودکار رخ داد.\n"
            "مدیر ما به زودی سفارش شما را پردازش خواهد کرد.",
        )


# ─── Delivery strategies ────────────────────────────────────────────

async def _deliver_ai_account(bot: Bot, user_id: int, order_id: int, product: str) -> None:
    platform = "chatgpt" if "ChatGPT" in product else "gemini"
    accounts = config.AI_ACCOUNTS.get(platform, [])

    if accounts:
        acc = random.choice(accounts)
        await _safe_send(
            bot,
            user_id,
            f"{get_pe('sparkles')} <b>پرداخت موفق!</b>\n\n"
            f"{get_pe('bot')} <b>اطلاعات ورود {product}:</b>\n\n"
            f"{get_pe('email_icon')} ایمیل: <code>{acc['email']}</code>\n"
            f"{get_pe('key_icon')} رمز عبور: <code>{acc['password']}</code>\n\n"
            f"{get_pe('warning')} لطفاً پس از ورود رمز عبور را تغییر دهید.\n"
            "برای پشتیبانی با @admin تماس بگیرید.",
        )
    else:
        await _safe_send(
            bot,
            user_id,
            f"{get_pe('check')} پرداخت موفق!\n\n"
            f"{get_pe('warning')} در حال حاضر اطلاعات ورود موجود نیست. "
            f"مدیر ما {product} شما را به زودی تحویل خواهد داد.",
        )


async def _deliver_wallet_recharge(
    bot: Bot, user_id: int, order_id: int, product: str, details: str
) -> None:
    """Deliver wallet recharge — add the paid amount to the user's wallet."""
    from database.db import add_to_wallet, get_order

    order = await get_order(order_id)
    if not order:
        logger.error("Wallet recharge delivery failed: order #%s not found", order_id)
        return

    amount = order["amount_irt"]
    if amount <= 0:
        logger.error("Wallet recharge delivery failed: invalid amount for order #%s", order_id)
        return

    await add_to_wallet(user_id, amount)

    from database.db import get_wallet_balance
    new_balance = await get_wallet_balance(user_id)
    amount_str = f"{amount:,}".replace(",", "،")
    balance_str = f"{new_balance:,}".replace(",", "،")

    await _safe_send(
        bot,
        user_id,
        f"{get_pe('check')} <b>پرداخت موفق!</b>\n\n"
        f"{get_pe('purse')} <b>کیف پول شما شارژ شد!</b>\n\n"
        f"{get_pe('money')} مبلغ شارژ: <b>{amount_str} تومان</b>\n"
        f"{get_pe('purse')} موجودی فعلی: <b>{balance_str} تومان</b>\n\n"
        f"{get_pe('sparkles')} از خرید شما متشکریم!",
    )


async def _deliver_design(
    bot: Bot, user_id: int, order_id: int, product: str, details: str
) -> None:
    await _safe_send(
        bot,
        user_id,
        f"{get_pe('sparkles')} <b>پرداخت موفق!</b>\n\n"
        f"{get_pe('fire')} درخواست طراحی شما ثبت شد!\n"
        "تیم طراحی ما درخواست شما را بررسی کرده و به زودی با شما تماس خواهد گرفت.",
    )

    # Notify admins
    order = await get_order(order_id)
    if order:
        admin_msg = (
            f"{get_pe('fire')} <b>سفارش طراحی جدید #{order['order_id']}</b>\n\n"
            f"{get_pe('user')} کاربر: <code>{order['user_id']}</code>\n"
            f"{get_pe('note')} جزئیات:\n{order['details']}\n\n"
            f"{get_pe('money')} مبلغ: {order['amount_irt']:,} تومان\n"
            "لطفاً این سفارش را پردازش کنید."
        )
        await _notify_admins(bot, admin_msg)


async def _deliver_virtual_number(
    bot: Bot, user_id: int, order_id: int, product_name: str, details: str
) -> None:
    """Deliver a purchased virtual number from the Kalino API — with auto-refund.

    Called ONLY after the user has paid (Zarinpal gateway or bot wallet).
    The bot first attempts the real purchase on the Kalino panel. If the
    panel fails to hand out a number (stock race condition), the FULL
    ``amount_irt`` is refunded to the user's wallet and both the user and
    the admins are notified. The bot never keeps money for a number it
    could not deliver.

    On success the number is sent together with two interactive buttons:
        📥 دریافت پیامک (کد)      → fetch the SMS code on demand
        ❌ لغو سفارش و بازگشت وجه  → cancel via API + auto-refund wallet

    ``details`` format (Kalino): "سرویس: {service_id} | کشور: {country} | تعداد: {qty}"
    """
    import re

    from utils.kalino import buy_virtual_number
    from utils import shiznumber

    order = await get_order(order_id)
    paid_amount = int(order["amount_irt"]) if order and order.get("amount_irt") else 0

    # ── Parse the Kalino order payload from the order details ──
    m_sid = re.search(r"سرویس:\s*(\S+)", details)
    m_country = re.search(r"کشور:\s*([^|]+)", details)
    m_qty = re.search(r"تعداد:\s*(\d+)", details)
    service_id = m_sid.group(1) if m_sid else "1"
    country = m_country.group(1).strip() if m_country else ""
    qty = max(1, int(m_qty.group(1))) if m_qty else 1

    # For ShizNumber, "country" contains the selected number_id.
    is_shiznumber = str(service_id) == "shiznumber"
    shiz_number_id = None

    if is_shiznumber:
        try:
            shiz_number_id = int(country)
        except (TypeError, ValueError):
            shiz_number_id = None

    if is_shiznumber and not shiz_number_id:
        logger.error(
            "ShizNumber: invalid number_id=%r for order #%s",
            country,
            order_id,
        )
        await _refund_auto(
            bot,
            user_id,
            order_id,
            paid_amount,
            "شناسه شماره نامعتبر است",
        )
        return

    if not is_shiznumber and not country:
        logger.error(
            "Virtual delivery: could not parse country from details='%s' (order #%s)",
            details, order_id,
        )
        await _refund_auto(bot, user_id, order_id, paid_amount, "کشور نامعتبر در جزئیات سفارش")
        return

    # ── Attempt the real panel purchase (one call per requested number) ──
    delivered: list[dict] = []
    failed = False

    for _ in range(qty):
        if is_shiznumber:
            # REAL ShizNumber purchase.
            # This is reached only after the shop payment succeeded.
            api_order = await asyncio.wait_for(
                shiznumber.buy_number(shiz_number_id),
                timeout=20,
            )

            if not isinstance(api_order, dict):
                failed = True
                reason = "بدون پاسخ از سرویس شماره"
                logger.warning(
                    "ShizNumber returned no response for order #%s",
                    order_id,
                )
                break

            if api_order.get("success") is False:
                failed = True
                reason = (
                    api_order.get("error")
                    or api_order.get("message")
                    or f"HTTP {api_order.get('status', '?')}"
                )
                logger.warning(
                    "ShizNumber purchase failed for order #%s: %s",
                    order_id,
                    reason,
                )
                break

            provider_order_id = (
                api_order.get("order", {}).get("id") if isinstance(api_order.get("order"), dict) else None
                or api_order.get("id")
                or api_order.get("order_id")
            )

            number = (
                api_order.get("order", {}).get("ordered_number") if isinstance(api_order.get("order"), dict) else None
                or api_order.get("ordered_number")
                or api_order.get("number")
            )

            if not provider_order_id or not number:
                failed = True
                reason = "پاسخ خرید شماره ناقص است"
                logger.warning(
                    "Invalid ShizNumber response for order #%s: %s",
                    order_id,
                    str(api_order)[:1000],
                )
                break

            delivered.append({
                "provider": "shiznumber",
                # Prefix prevents collision with Kalino request IDs.
                "request_id": f"shiz:{provider_order_id}",
                "order_id": provider_order_id,
                "number": number,
                "raw": api_order,
            })

        else:
            api_order = await asyncio.wait_for(
                buy_virtual_number(service_id, country),
                timeout=20,
            )

            if (
                not api_order
                or not api_order.get("success")
                or not api_order.get("request_id")
            ):
                failed = True
                reason = (
                    (api_order or {}).get("error_msg")
                    or (api_order or {}).get("error_code")
                    or "بدون پاسخ"
                )
                logger.warning(
                    "Kalino could not deliver a number for order #%s: %s",
                    order_id,
                    reason,
                )
                break

            delivered.append(api_order)

        await asyncio.sleep(0.5)

    if failed:
        # Cancel any partially-bought numbers in this batch, then refund 100%.
        if is_shiznumber:
            for d in delivered:
                with contextlib.suppress(Exception):
                    provider_order_id = d.get("order_id") or d.get("request_id")
                    if provider_order_id:
                        await shiznumber.cancel_order(int(provider_order_id))
        else:
            from utils.kalino import cancel_virtual_number
            for d in delivered:
                with contextlib.suppress(Exception):
                    await cancel_virtual_number(d.get("request_id"))

        await _refund_auto(
            bot,
            user_id,
            order_id,
            paid_amount,
            (delivered and "بخشی از سفارش تحویل نشد") or "موجودی تمام شد",
        )
        return

    # ── Success: mark delivered and present each number interactively ──
    await update_order_status(order_id, "delivered")

    for i, d in enumerate(delivered, start=1):
        request_id = d.get("request_id")
        number = d.get("number") or "?"

        text = (
            f"{get_pe('sparkles')} <b>پرداخت موفق!</b>\n\n"
            f"{get_pe('key_lock')} <b>شماره مجازی شما:</b>\n"
            f"{get_pe('mobile')} شماره: <code>{number}</code>\n"
            f"{get_pe('earth')} سرویس: <b>{service_id}</b> | کشور: <b>{country}</b>\n"
            f"{get_pe('money')} مبلغ پرداخت: <b>{paid_amount:,} تومان</b>\n\n"
            f"{get_pe('hourglass')} کد تأیید معمولاً ظرف ۱ تا ۲ دقیقه می‌رسد.\n"
            f"{get_pe('warning')} هر شماره تا <b>۱۵ دقیقه</b> اعتبار دارد."
        )
        kb = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(
                text=f"{get_plain_emoji('inbox')} دریافت پیامک (کد)",
                callback_data=f"vsms:{order_id}:{request_id}:{number}",
            )],
            [InlineKeyboardButton(
                text=f"{get_plain_emoji('cross')} لغو سفارش و بازگشت وجه",
                callback_data=f"vcnl:{order_id}:{request_id}:{number}",
            )],
            [InlineKeyboardButton(
                text=f"{get_plain_emoji('back_arrow')} بازگشت به منو",
                callback_data="menu:back_main",
            )],
        ])
        await bot.send_message(user_id, text, reply_markup=kb)

    # Notify admins with the accounting summary when a number is delivered.
    from utils.kalino import get_panel_balance
    panel_balance = await get_panel_balance()
    await _notify_admins(
        bot,
        f"{get_pe('box')} <b>شماره مجازی تحویل شد — سفارش #{order_id}</b>\n\n"
        f"{get_pe('user')} کاربر: <code>{user_id}</code>\n"
        f"{get_pe('earth')} سرویس/کشور: <b>{service_id}</b> / <b>{country}</b> | تعداد: <b>{qty}</b>\n"
        f"{get_pe('money')} مبلغ: <b>{paid_amount:,}</b> تومان\n"
        f"{get_pe('box')} موجودی پنل: <b>{panel_balance:,}</b> تومان",
    )


async def _refund_auto(
    bot: Bot,
    user_id: int,
    order_id: int,
    refund_amount: int,
    reason: str = "",
) -> None:
    """Fail-safe auto-refund: cancel the order + credit the local wallet."""
    from database.db import update_order_status, add_to_wallet

    await update_order_status(order_id, "cancelled")

    if refund_amount > 0:
        await add_to_wallet(user_id, refund_amount)

    refund_str = f"{refund_amount:,}".replace(",", "،")
    message = (
        f"{get_pe('cross')} <b>موجودی شماره در لحظه آخر به پایان رسید!</b>\n\n"
        f"کاربر گرامی، از آنجا که موجودی شماره‌های مجازی در سرورهای جهانی به‌صورت لحظه‌ای تغییر می‌کند، دقیقاً در زمان پرداخت شما، این شماره توسط شخص دیگری در شبکه خریداری شد و موجودی آن موقتاً صفر شد.\n\n"
        f"{get_pe('check')} <b>جای نگرانی نیست!</b>\n"
        f"مبلغ <b>{refund_str} تومان</b> پرداختی شما به‌صورت کامل و اتوماتیک به <b>کیف پول شما در ربات</b> بازگشت داده شد.\n"
        f"هم‌اکنون می‌توانید بدون نیاز به درگاه، از موجودی کیف پول خود برای خرید کشور دیگری استفاده کنید."
    )
    await _safe_send(bot, user_id, message)

    await _notify_admins(
        bot,
        f"{get_pe('siren')} <b>کنسلی خودکار سفارش #{order_id}</b>\n"
        f"مبلغ <b>{refund_str} تومان</b> به کیف پول کاربر <code>{user_id}</code> بازگشت داده شد.\n"
        f"دلیل: {reason or 'خطای تحویل پنل کالینو (Kalino)'}",
    )


async def _deliver_generic(bot: Bot, user_id: int, order_id: int, product: str) -> None:
    await _safe_send(
        bot,
        user_id,
        f"{get_pe('sparkles')} <b>پرداخت موفق!</b>\n\n"
        f"سفارش <b>{product}</b> شما تأیید شد.\n\n"
        "فعال‌سازی اشتراک‌های پرمیوم به زودی انجام خواهد شد.\n"
        "برای پشتیبانی با @admin تماس بگیرید.",
    )

    order = await get_order(order_id)
    if order:
        admin_msg = (
            f"{get_pe('money')} <b>سفارش پرداخت شده #{order['order_id']}</b>\n\n"
            f"محصول: {order['product']}\n"
            f"جزئیات: {order['details']}\n"
            f"مبلغ: {order['amount_irt']:,} تومان\n"
            f"کاربر: <code>{order['user_id']}</code>\n\n"
            "لطفاً این سفارش را پردازش کنید."
        )
        await _notify_admins(bot, admin_msg)


# ─── Helpers ─────────────────────────────────────────────────────────

async def _safe_send(bot: Bot, user_id: int, text: str) -> None:
    """Send a message, silently swallowing Telegram API errors."""
    try:
        await bot.send_message(user_id, text)
    except Exception as exc:
        logger.warning("Could not send message to %s: %s", user_id, exc)


async def _notify_admins(bot: Bot, text: str) -> None:
    for admin_id in config.ADMIN_IDS:
        await _safe_send(bot, admin_id, text)


async def _alert_delivery_failure(
    bot: Bot,
    user_id: int,
    order_id: int,
    product: str,
    error_msg: str,
    extra_info: str = "",
) -> None:
    """Alert both the user and admin when Kalino delivery fails.

    The user is told their payment succeeded but delivery had an issue.
    The admin receives full transaction details for manual resolution.
    """
    from database.db import get_order

    # 1. Notify the user
    await _safe_send(
        bot,
        user_id,
        f"{get_pe('warning')} <b>پرداخت شما موفق بود اما در تحویل خودکار مشکلی پیش آمد.</b>\n\n"
        f"تیکت شما برای پشتیبانی ارسال شد و اکانت به زودی تحویل می‌گردد.\n\n"
        f"{get_pe('ticket')} شماره سفارش: <code>#{order_id}</code>\n"
        f"{get_pe('call')} لطفاً منتظر پاسخ پشتیبانی باشید.",
    )

    # 2. Notify all admins with full details
    order = await get_order(order_id)
    order_info = ""
    if order:
        order_info = (
            f"{get_pe('box')} شماره سفارش: #{order['order_id']}\n"
            f"{get_pe('user')} کاربر: <code>{order['user_id']}</code>\n"
            f"{get_pe('shopping')} محصول: {order['product']}\n"
            f"{get_pe('note')} جزئیات: {order['details']}\n"
            f"{get_pe('money')} مبلغ: {order['amount_irt']:,} تومان\n"
        )

    admin_msg = (
        f"{get_pe('cross')} <b>خطا در تحویل خودکار سفارش #{order_id}</b>\n\n"
        f"{order_info}\n"
        f"{get_pe('warning')} خطا: {error_msg}\n"
    )
    if extra_info:
        admin_msg += f"\n{get_pe('note')} اطلاعات اضافی:\n{extra_info}\n"

    admin_msg += (
        f"\n{get_pe('call')} لطفاً سفارش را به صورت دستی پردازش کنید."
    )

    await _notify_admins(bot, admin_msg)
    logger.warning(
        "Kalino delivery failure for order #%s (user %s): %s",
        order_id, user_id, error_msg,
    )
