from __future__ import annotations

import asyncio
import html
import logging
import re
import unicodedata
from datetime import datetime
from typing import Any
from urllib.parse import urlencode
from zoneinfo import ZoneInfo

import aiohttp
import jdatetime
from telegram import (
    Bot,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    ReactionTypeEmoji,
    Update,
)
from telegram.constants import ParseMode
from telegram.ext import (
    Application,
    CallbackQueryHandler,
    CommandHandler,
    ContextTypes,
    MessageHandler,
    filters,
)

import config
import database as db_mod

logging.basicConfig(
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    level=logging.INFO,
)
log = logging.getLogger(__name__)

try:
    from telegram import CopyTextButton
    _COPY_SUPPORTED = True
except ImportError:
    CopyTextButton = None
    _COPY_SUPPORTED = False

try:
    _test_btn = InlineKeyboardButton(
        text="t", callback_data="t",
        api_kwargs={"style": "primary"}
    )
    _STYLE_SUPPORTED = True
except Exception:
    _STYLE_SUPPORTED = False
_STYLE_SUPPORTED = True

PRODUCT_TYPE_TELEGRAM_PREMIUM = "telegram_premium"
PRODUCT_TYPE_TELEGRAM_STARS   = "telegram_stars"
PRODUCT_TYPE_CHATGPT          = "chatgpt"
PRODUCT_TYPE_GEMINI           = "gemini"
PRODUCT_TYPE_VIRTUAL_NUMBER   = "virtual_number"
PRODUCT_TYPE_BOOST            = "boost"
PRODUCT_TYPE_FRAGMENT         = "fragment"
PRODUCT_TYPE_GENERIC          = "generic"

ACTIVATION_PRODUCT_TYPES = (
    PRODUCT_TYPE_TELEGRAM_PREMIUM,
    PRODUCT_TYPE_TELEGRAM_STARS,
    PRODUCT_TYPE_BOOST,
)

TRUST_COMMENT_TEXT = (
    "کاملاً به شما حق می‌دهیم؛ امنیت خرید و اعتماد در فضای مجازی بالاترین اولویت را دارد.\n\n"
    "ما برای شفافیت کامل و اطمینان خاطر شما، هیچ واسطه‌ای قرار نداده‌ایم! تمام کسانی که پیش از این "
    "از فروشگاه ما خرید کرده‌اند، تجربه‌ و نظرات خود را مستقیماً در بخش کامنت‌های پست زیر ثبت کرده‌اند.\n\n"
    "شما می‌توانید نظرات مشتریان قبلی ما را بخوانید و حتی در صورت تمایل، برای اطمینان بیشتر به خود آن‌ها "
    "پیام دهید و کیفیت خدمات ما را جویا شوید.\n\n"
    "👇 از طریق لینک زیر می‌توانید وارد پست نظرات و رضایت مشتریان شوید:\n"
    "https://t.me/ReinPremium/2185"
)

SUPPORT_COMMENT_TEXT = (
    "پاسخگوی سوالات شما هستیم 💎\n\n"
    "کاربر گرامی، اگر در خصوص سرویس‌ها، قیمت‌ها، نحوه فعال‌سازی یا هر بخش دیگری سوال، ابهام یا نیازی "
    "به راهنمایی دارید، تیم پشتیبانی ما آماده پاسخگویی به شماست.\n\n"
    "برای ارتباط مستقیم با ما، لطفاً به منوی اصلی ربات برگردید و دکمه «پشتیبانی» را لمس کنید تا پیام شما "
    "مستقیماً به دست پشتیبان ربات برسد.\n\n"
    "در کوتاه‌ترین زمان ممکن پاسخگوی شما خواهیم بود. از همراهی شما سپاسگزاریم. 🙏✨"
)

ORDER_SUCCESS_USER_TEXT = (
    "🎉 <b>سفارش شما با موفقیت تکمیل شد!</b>\n\n"
    "✅ وضعیت: <b>تکمیل شده</b>\n"
    "🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
    "از خرید شما سپاسگزاریم. 🙏"
)


def _detect_product_type(name: str, desc: str = "") -> str:
    combined = (name + " " + (desc or "")).lower()
    if any(k in combined for k in ("stars", "استارز", "استار")):
        return PRODUCT_TYPE_TELEGRAM_STARS
    if any(k in combined for k in (
        "پریمیوم", "premium", "تلگرام پریم", "premi"
    )):
        return PRODUCT_TYPE_TELEGRAM_PREMIUM
    if any(k in combined for k in ("فرگمنت", "fragment")):
        return PRODUCT_TYPE_FRAGMENT
    if any(k in combined for k in (
        "chatgpt", "chat gpt", "چت جی پی تی", "چتgpt",
        "openai", "open ai", "gpt-4", "gpt4", "gpt 4",
        "جی پی تی", "gpt"
    )):
        return PRODUCT_TYPE_CHATGPT
    if any(k in combined for k in (
        "gemini", "جمینای", "گوگل جمینای", "google gemini",
        "bard", "google ai", "گوگل ای آی"
    )):
        return PRODUCT_TYPE_GEMINI
    if any(k in combined for k in (
        "شماره مجازی", "virtual number", "شماره موقت",
        "sms activate", "virtual sim", "شماره"
    )):
        return PRODUCT_TYPE_VIRTUAL_NUMBER
    if any(k in combined for k in ("boost", "بوست")):
        return PRODUCT_TYPE_BOOST
    return PRODUCT_TYPE_GENERIC


def _needs_extra_info_before_checkout(ptype: str) -> bool:
    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST):
        return True
    return _extra_info_required(ptype) is not None


def _extra_info_required(product_type: str) -> str | None:
    if product_type == PRODUCT_TYPE_TELEGRAM_PREMIUM:
        return "📱 <b>تلگرام پریمیوم</b>\n\nیوزرنیم تلگرام خود را وارد کنید:\nمثال: <code>@myusername</code>"
    if product_type == PRODUCT_TYPE_TELEGRAM_STARS:
        return "⭐ <b>استارز تلگرام</b>\n\nیوزرنیم تلگرام را وارد کنید:\nمثال: <code>@myusername</code>"
    if product_type == PRODUCT_TYPE_CHATGPT:
        return "🤖 <b>اکانت ChatGPT</b>\n\nآدرس ایمیل خود را وارد کنید:\nمثال: <code>example@gmail.com</code>"
    if product_type == PRODUCT_TYPE_VIRTUAL_NUMBER:
        return "📞 <b>شماره مجازی</b>\n\nکشور مورد نظر و سرویس مورد نیاز را وارد کنید:\nمثال: <code>ایران - واتساپ</code>"
    if product_type == PRODUCT_TYPE_BOOST:
        return "🚀 <b>بوست کانال / گروه</b>\n\nآیدی کانال یا گروه را وارد کنید:\nمثال: <code>@mychannel</code> یا <code>-1001234567890</code>"
    return None


KYC_INTRO_TEXT = (
    "👤 مشتری گرامی، ضمن تشکر از انتخاب شما؛"
    "جهت پیشگیری از سوءاستفاده‌های احتمالی و مطابق با پروتکل‌های امنیتی پرداخت‌های آنلاین، "
    "انجام احراز هویت برای اولین خرید الزامی است. "
    "این فرآیند صرفاً جهت اطمینان از مالکیت کارت بانکی و جلوگیری از تراکنش‌های غیرمجاز (فیشینگ) "
    "انجام می‌شود تا امنیت حساب شما و فروشگاه حفظ گردد. "
    "پس از تایید، خریدهای آتی شما بدون معطلی انجام خواهد شد ✅"
    "\n📌 <a href='https://t.me/ReinPremium/2772'>چطور میتونم در ارائه مدارک مربوطه به شما اعتماد کنم؟</a>"
)

KYC_INSTRUCTION_TEXT = (
    "📝 <b>شرایط احراز هویت :</b>\n"
    "لطفاً روی یک قطعه کاغذ عبارت «<b>خرید از آیدی @ReinWorkShop_bot</b>» را به همراه "
    "«<b>تاریخ امروز</b>» بنویسید. "
    "سپس کاغذ را در کنار کارت بانکی که قصد پرداخت با آن را دارید قرار داده و عکس بگیرید.\n\n"
    "<b>نکته امنیتی:</b> تنها مشخص بودن نام صاحب کارت و شماره ۱۶ رقمی کارت کافی است. "
    "برای حفظ حریم خصوصی خود، حتماً روی بخش‌های حساس (CVV2 و تاریخ انقضا) را بپوشانید.\n\n"
    "لطفاً عکس را در پاسخ (Reply) به همین پیام ارسال کنید. 👇"
)


EMOJI_GEMINI    = "5420321759175927595"
EMOJI_CHATGPT   = "5420144583185033597"
EMOJI_STARS     = "5388615365704048764"
EMOJI_BOOST     = "5264725380488517445"
EMOJI_PREMIUM   = "5265234660530607401"
EMOJI_FRAGMENT  = "5413634774268934356" 
EMOJI_AI        = "5282790962123265091"  
EMOJI_BACK_MAIN  = "5348064629547351986"  
EMOJI_BACK       = "5346112927688574301"  
EMOJI_CANCEL     = "6030757850274336631"  
EMOJI_ORDER      = "5346214473600352322"  
EMOJI_WELCOME    = "5247133031235329609"  
EMOJI_COPY_LINK  = "5348263477943222701" 


def _btn(text: str, *, cb: str | None = None, url: str | None = None,
         style: str | None = None, emoji_id: str | None = None) -> InlineKeyboardButton:
    no_color = isinstance(style, str) and style.strip().lower() in ("none", "default", "normal")
    if emoji_id:
        emoji_id = emoji_id.strip()
        if not emoji_id.isdigit():
            emoji_id = None

    btn_text = text
    if emoji_id:
        cleaned = btn_text.lstrip()
        i = 0
        while i < len(cleaned):
            cp = ord(cleaned[i])
            if (0x1F300 <= cp <= 0x1FAFF or  
                0x2600  <= cp <= 0x27BF or   
                0xFE00  <= cp <= 0xFE0F or   
                0x200D == cp or            
                0x20E3 == cp):            
                i += 1
                while i < len(cleaned) and ord(cleaned[i]) in (0xFE0F, 0x20E3, 0x200D):
                    i += 1
            elif cleaned[i] in (' ', '\u00a0', '\u200b', '─', '—'):
                i += 1
            else:
                break
        btn_text = cleaned[i:].strip()
        if not btn_text:
            btn_text = text

    if style is None and not no_color:
        t_low = btn_text.lower()
        if any(k in t_low for k in (
            "انصراف", "بازگشت", "حذف", "لغو", "خروج", "بن کردن", "بستن",
        )):
            style = "danger"
        elif any(k in t_low for k in (
            "خرید", "پرداخت", "ثبت سفارش", "تأیید", "افزودن",
            "فعال", "شارژ", "واریز", "ارسال", "تحویل", "افزایش", "بله",
        )):
            style = "success"
        else:
            style = "primary"

    kwargs: dict = {"text": btn_text}
    if cb:
        kwargs["callback_data"] = cb
    elif url:
        kwargs["url"] = url
    else:
        kwargs["callback_data"] = "noop"

    extra: dict = {}
    if not no_color:
        extra["style"] = style
    if emoji_id:
        extra["icon_custom_emoji_id"] = emoji_id
    if extra:
        kwargs["api_kwargs"] = extra

    return InlineKeyboardButton(**kwargs)


def _copy_btn(text: str, copy_value: str,
              style: str | None = "primary",
              emoji_id: str | None = None) -> InlineKeyboardButton:
    safe = (copy_value or "")[:256]
    btn_text = text
    if emoji_id:
        cleaned = text.lstrip()
        i = 0
        while i < len(cleaned):
            cp = ord(cleaned[i])
            if (0x1F300 <= cp <= 0x1FAFF or 0x2600 <= cp <= 0x27BF or
                    0xFE00 <= cp <= 0xFE0F or cp in (0x200D, 0x20E3)):
                i += 1
            elif cleaned[i] in (' ', '\u00a0'):
                i += 1
            else:
                break
        btn_text = cleaned[i:].strip() or text

    kwargs: dict = {"text": btn_text}
    if _COPY_SUPPORTED and CopyTextButton is not None:
        kwargs["copy_text"] = CopyTextButton(text=safe)
    else:
        kwargs["copy_text"] = {"text": safe}
    extra: dict = {"style": style or "primary"}
    if emoji_id and emoji_id.strip().isdigit():
        extra["icon_custom_emoji_id"] = emoji_id.strip()
    kwargs["api_kwargs"] = extra
    return InlineKeyboardButton(**kwargs)


def _kb(*rows: list[InlineKeyboardButton]) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(list(rows))


def _he(value: Any) -> str:
    return html.escape(str(value) if value is not None else "")


def _row_get(row: Any, key: str, default: Any = "") -> Any:
    if row is None:
        return default
    if isinstance(row, dict):
        return row.get(key, default)
    try:
        return row[key]
    except (KeyError, IndexError, TypeError):
        return default


def _order_admin_kb(order_id: int) -> InlineKeyboardMarkup:
    oid = str(int(order_id))
    return InlineKeyboardMarkup([
        [
            InlineKeyboardButton(
                "✅ تایید و ارسال محصول",
                callback_data=f"adm_confirm_order_{oid}",
            ),
        ],
        [
            InlineKeyboardButton(
                "❌ رد سفارش",
                callback_data=f"adm_reject_order_{oid}",
            ),
        ],
    ])


def _order_id_from_callback(data: str, action: str) -> int:
    if action == "confirm":
        candidates = ("adm_confirm_order_", "ao_")
    else:
        candidates = ("adm_reject_order_", "ar_")
    for prefix in candidates:
        if data.startswith(prefix):
            suffix = data[len(prefix):].strip()
            if suffix.isdigit():
                return int(suffix)
    raise ValueError(f"invalid callback for {action}: {data!r}")


def _set_admin_delivery_ctx(ctx: ContextTypes.DEFAULT_TYPE, admin_id: int, order_id: int) -> None:
    ctx.user_data["adm_state"] = f"adm_deliver_{order_id}"
    pending = ctx.application.bot_data.setdefault("adm_pending_delivery", {})
    pending[admin_id] = order_id


def _get_admin_delivery_order_id(ctx: ContextTypes.DEFAULT_TYPE, admin_id: int) -> int | None:
    state = ctx.user_data.get("adm_state", "")
    if state.startswith("adm_deliver_"):
        try:
            return int(state.rsplit("_", 1)[-1])
        except ValueError:
            pass
    pending = ctx.application.bot_data.get("adm_pending_delivery", {})
    oid = pending.get(admin_id)
    return int(oid) if oid is not None else None


def _clear_admin_delivery_ctx(ctx: ContextTypes.DEFAULT_TYPE, admin_id: int) -> None:
    ctx.user_data.pop("adm_state", None)
    pending = ctx.application.bot_data.get("adm_pending_delivery")
    if pending and admin_id in pending:
        del pending[admin_id]


async def _react(bot: Bot, chat_id: int, message_id: int, emoji: str = "👍") -> None:
    try:
        await bot.set_message_reaction(chat_id=chat_id, message_id=message_id,
                                       reaction=[ReactionTypeEmoji(emoji=emoji)])
    except Exception:
        pass

def _jalali_now() -> str:
    jdt = jdatetime.datetime.fromgregorian(datetime=datetime.now(ZoneInfo("Asia/Tehran")))
    DAYS = ["دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"]
    MONTHS = ["فروردین","اردیبهشت","خرداد","تیر","مرداد","شهریور",
               "مهر","آبان","آذر","دی","بهمن","اسفند"]
    return f"{DAYS[jdt.weekday()]} {jdt.day} {MONTHS[jdt.month-1]} {jdt.year} ساعت {jdt.hour:02d}:{jdt.minute:02d}"


def _jalali_dt_now() -> jdatetime.datetime:
    return jdatetime.datetime.fromgregorian(datetime=datetime.now(ZoneInfo("Asia/Tehran")))


def _is_valid_telegram_numeric_id(value: str) -> bool:
    v = value.strip()
    if not re.fullmatch(r"-?\d{5,20}", v):
        return False
    return v != "0"


def _is_valid_telegram_channel_ref(value: str) -> bool:
    v = value.strip()
    if re.fullmatch(r"@[\w\d_]{4,32}", v):
        return True
    return bool(re.fullmatch(r"-100\d{5,20}", v))


def _is_valid_telegram_username(value: str) -> bool:
    v = value.strip().lstrip("@")
    return bool(re.fullmatch(r"[\w\d_]{4,32}", v))


def _is_activation_product(ptype: str) -> bool:
    return ptype in ACTIVATION_PRODUCT_TYPES


def _qty_unit_label(ptype: str) -> str:
    if ptype == PRODUCT_TYPE_TELEGRAM_STARS:
        return "استارز"
    if ptype == PRODUCT_TYPE_BOOST:
        return "بوست"
    return "عدد"


def _order_extra_info_kb(product_id: int, ptype: str) -> InlineKeyboardMarkup:
    rows: list[list[InlineKeyboardButton]] = []
    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_TELEGRAM_PREMIUM):
        rows.append([_btn("👤 برای خودم", cb=f"order_for_myself_{product_id}")])
    rows.append([_btn("بازگشت", cb=f"product_{product_id}")])
    return InlineKeyboardMarkup(rows)


def _qty_summary_price_line(pending: dict, quantity: int, unit_label: str) -> str:
    total = int(pending.get("total_irt") or 0)
    return f"💰 قیمت <b>{quantity:,}</b> {unit_label}: <b>{total:,} تومان</b>\n"


def _pending_price_block(pending: dict) -> str:
    if not pending or not pending.get("total_irt"):
        return ""
    total = int(pending["total_irt"])
    base = int(pending.get("base_price_irt") or pending.get("price_irt") or total)
    fee = int(pending.get("fee_irt") or 0)
    discount = int(pending.get("discount_irt") or 0)
    ptype = pending.get("product_type", "")
    qty = int(pending.get("quantity") or 1)
    unit = int(pending.get("unit_price_irt") or 0)
    lines: list[str] = []
    if qty > 1 and unit > 0 and ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST):
        lines.append(f"💵 قیمت هر واحد: <b>{unit:,} تومان</b>")
        lines.append(f"💰 جمع پایه: <b>{base:,} تومان</b>")
    elif base > 0:
        lines.append(f"💰 قیمت پایه: <b>{base:,} تومان</b>")
    if fee > 0:
        lines.append(f"💸 کارمزد: <b>{fee:,} تومان</b>")
    if discount > 0:
        lines.append(f"🎟 تخفیف: <b>-{discount:,} تومان</b>")
    lines.append(f"✅ <b>مبلغ نهایی: {total:,} تومان</b>")
    return "\n".join(lines) + "\n"


def _username_confirm_kb(product_id: int, ptype: str) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup([
        [_btn("✅ بله", cb="confirm_username_yes"),
         _btn("❌ خیر", cb=f"confirm_username_no_{product_id}")],
    ])


def _build_username_entry_text(pending: dict, ptype: str) -> str:
    quantity = int(pending.get("quantity") or 1)
    price_block = _pending_price_block(pending)
    if ptype == PRODUCT_TYPE_TELEGRAM_STARS:
        unit_label = "استارز"
        return (
            f"⭐ <b>استارز تلگرام</b>\n\n"
            f"🔢 تعداد: <b>{quantity:,} {unit_label}</b>\n\n"
            f"{price_block}"
            "یوزرنیم تلگرام را وارد کنید:\n"
            "مثال: <code>@myusername</code>"
        )
    if ptype == PRODUCT_TYPE_BOOST:
        return (
            f"🚀 <b>بوست کانال / گروه</b>\n\n"
            f"🔢 تعداد بوست: <b>{quantity:,}</b>\n\n"
            f"{price_block}"
            "یوزرنیم کانال یا گروه را وارد کنید:\n"
            "مثال: <code>@mychannel</code>"
        )
    if ptype == PRODUCT_TYPE_TELEGRAM_PREMIUM:
        return (
            "📱 <b>تلگرام پریمیوم</b>\n\n"
            "یوزرنیم تلگرام خود را وارد کنید:\n"
            "مثال: <code>@myusername</code>\n\n"
            f"{price_block}"
        )
    extra_prompt = _extra_info_required(ptype) or "اطلاعات را وارد کنید:"
    return f"{extra_prompt}\n\n{price_block}"


def _checkout_payment_rows(product_id: int) -> list[list[InlineKeyboardButton]]:
    pay_rows: list[list[InlineKeyboardButton]] = []
    if db_mod.is_section_active("topup_card"):
        pay_rows.append([_btn("💳 پرداخت کارت به کارت", cb="checkout_pay_card")])
    if db_mod.is_section_active("topup_crypto"):
        pay_rows.append([_btn("💰 پرداخت ارزی", cb="checkout_pay_crypto")])
    if db_mod.is_section_active("topup_gateway"):
        pay_rows.append([_btn("🌐 پرداخت درگاهی", cb="checkout_pay_gateway")])
    pay_rows.append([_btn("بازگشت", cb=f"product_{product_id}")])
    return pay_rows


def _build_checkout_payment_text(
    pending: dict, extra_info: str | None, balance: int,
) -> str:
    total_irt = int(pending.get("total_irt") or 0)
    shortage = max(0, total_irt - balance)
    ptype = pending.get("product_type", PRODUCT_TYPE_GENERIC)
    product_id = pending.get("product_id")
    p = db_mod.get_product(product_id) if product_id else None
    product_name = p["name"] if p else "محصول"
    quantity = int(pending.get("quantity") or 1)
    parts = [
        "💳 <b>پرداخت و ثبت سفارش</b>\n",
        f"📦 محصول: <b>{_he(product_name)}</b>",
    ]
    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST) and quantity >= 1:
        parts.append(f"🔢 تعداد: <b>{quantity:,} {_qty_unit_label(ptype)}</b>")
    extra_line = _build_extra_line(ptype, extra_info).strip()
    if extra_line:
        parts.append(extra_line)
    price_block = _pending_price_block(pending).strip()
    if price_block:
        parts.append(price_block)
    parts.append(f"💳 موجودی شما: <b>{balance:,} تومان</b>")
    parts.append(f"📉 مبلغ مورد نیاز: <b>{shortage:,} تومان</b>")
    parts.append(
        "\nروش پرداخت را انتخاب کنید. پس از تأیید واریز، سفارش شما <b>خودکار</b> ثبت می‌شود."
    )
    return "\n".join(parts)


async def _send_username_warning_confirm(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE,
    extra_info: str, ptype: str, *, via_callback: bool = False,
) -> None:
    pending = ctx.user_data.get("pending_order", {})
    product_id = pending.get("product_id")
    label = "یوزرنیم" if ptype != PRODUCT_TYPE_BOOST else "یوزرنیم کانال/گروه"
    ctx.user_data["username_confirm"] = extra_info
    ctx.user_data["state"] = "awaiting_username_confirm"
    price_block = _pending_price_block(pending)
    text = (
        f"⚠️ <b>تأیید {label}</b>\n\n"
        f"مقدار واردشده: <code>{_he(extra_info)}</code>\n\n"
        f"{price_block}"
        "لطفاً صحت اطلاعات را بررسی کنید.\n"
        "<b>در صورت اشتباه بودن آیدی، مسئولیت بر عهده خود شماست.</b>\n\n"
        "آیا از صحت این آیدی مطمئن هستید؟"
    )
    kb = _username_confirm_kb(product_id, ptype)
    if via_callback:
        sent = await update.callback_query.message.reply_text(
            text, reply_markup=kb, parse_mode=ParseMode.HTML,
        )
    else:
        sent = await update.message.reply_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)
    ctx.user_data["username_warn_msg_id"] = sent.message_id
    ctx.user_data["username_warn_chat_id"] = sent.chat_id


async def _delete_username_warning_message(ctx: ContextTypes.DEFAULT_TYPE) -> None:
    msg_id = ctx.user_data.pop("username_warn_msg_id", None)
    chat_id = ctx.user_data.pop("username_warn_chat_id", None)
    if msg_id and chat_id:
        try:
            await ctx.bot.delete_message(chat_id=chat_id, message_id=msg_id)
        except Exception:
            pass


async def _edit_callback_message(
    q: Any, text: str, reply_markup: InlineKeyboardMarkup | None = None,
) -> None:
    last_err: Exception | None = None
    try:
        await q.edit_message_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
        return
    except Exception as e:
        last_err = e
    try:
        await q.edit_message_caption(
            caption=text, reply_markup=reply_markup, parse_mode=ParseMode.HTML,
        )
        return
    except Exception as e:
        last_err = e
    await q.message.reply_text(text, reply_markup=reply_markup, parse_mode=ParseMode.HTML)
    if last_err:
        log.debug("edit_callback fallback reply: %s", last_err)


def _qty_line_for_order(ptype: str, quantity: int) -> str:
    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST) and quantity >= 1:
        return f"🔢 تعداد: <b>{quantity:,} {_qty_unit_label(ptype)}</b>\n"
    if quantity > 1:
        return f"🔢 تعداد: <b>{quantity:,} {_qty_unit_label(ptype)}</b>\n"
    return ""


async def _notify_admins(
    ctx: ContextTypes.DEFAULT_TYPE,
    notif_key: str,
    text: str,
    reply_markup: InlineKeyboardMarkup | None = None,
    *,
    photo_file_id: str | None = None,
) -> None:
    for admin_id in db_mod.get_admins_for_notification(notif_key):
        try:
            if photo_file_id:
                await ctx.bot.send_photo(
                    admin_id, photo=photo_file_id, caption=text,
                    reply_markup=reply_markup, parse_mode=ParseMode.HTML,
                )
            else:
                await ctx.bot.send_message(
                    admin_id, text, reply_markup=reply_markup, parse_mode=ParseMode.HTML,
                )
        except Exception as e:
            log.warning("اعلان به ادمین %s ناموفق (%s): %s", admin_id, notif_key, e)


def _normalize_ascii_digits(value: str) -> str:
    return str(value or "").translate(str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789"))


def _is_telegram_api_product(ptype: str) -> bool:
    return ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_TELEGRAM_PREMIUM)


def _telegram_premium_months_from_product(product: Any) -> int | None:
    name = _row_get(product, "name", "")
    desc = _row_get(product, "description", "") or ""
    text = _normalize_ascii_digits(f"{name} {desc}").lower()
    month_words = (
        (12, ("12", "دوازده", "یک سال", "يک سال", "یکساله", "يکساله", "سالانه", "سالیانه")),
        (6, ("6", "شش", "نیم سال", "نيم سال")),
        (3, ("3", "سه")),
    )
    for months, words in month_words:
        for word in words:
            if word.isdigit():
                if re.search(rf"(?<!\d){word}(?!\d)", text):
                    return months
            elif word in text:
                return months
    return None


def _telegram_api_service_id(ptype: str, product: Any) -> int | None:
    custom_map = getattr(config, "TELEGRAM_API_SERVICE_IDS", None)
    if isinstance(custom_map, dict):
        key = "stars" if ptype == PRODUCT_TYPE_TELEGRAM_STARS else None
        if key and custom_map.get(key):
            try:
                return int(custom_map[key])
            except Exception:
                pass
    if ptype == PRODUCT_TYPE_TELEGRAM_STARS:
        return 1
    if ptype == PRODUCT_TYPE_TELEGRAM_PREMIUM:
        months = _telegram_premium_months_from_product(product)
        if isinstance(custom_map, dict) and months and custom_map.get(f"premium_{months}"):
            try:
                return int(custom_map[f"premium_{months}"])
            except Exception:
                pass
        return {3: 2, 6: 3, 12: 4}.get(months)
    return None


def _telegram_api_order_link(extra_info: str | None) -> str:
    value = (extra_info or "").strip()
    if value.startswith("https://t.me/"):
        username = value[len("https://t.me/"):].split("?", 1)[0].split("/", 1)[0]
    elif value.startswith("http://t.me/"):
        username = value[len("http://t.me/"):].split("?", 1)[0].split("/", 1)[0]
    elif value.startswith("t.me/"):
        username = value[len("t.me/"):].split("?", 1)[0].split("/", 1)[0]
    else:
        username = value.lstrip("@").split("?", 1)[0].split("/", 1)[0]
    username = username.strip()
    if not _is_valid_telegram_username(username):
        raise ValueError("یوزرنیم تلگرام برای ارسال به API معتبر نیست")
    return f"https://t.me/{username}"


def _build_api_get_url(api_url: str, params: dict[str, Any]) -> str:
    base = api_url.strip()
    query = urlencode(params, doseq=False, safe=":/@")
    if "?" in base:
        sep = "" if base.endswith(("?", "&")) else "&"
        return f"{base}{sep}{query}"
    return f"{base.rstrip('/')}/?{query}"


def _mask_api_secret(text: str) -> str:
    return re.sub(r"(key=)[^&\s]+", r"\1***", str(text or ""))


def _clip_api_text(text: Any, limit: int = 2800) -> str:
    value = _mask_api_secret(str(text if text is not None else ""))
    if len(value) <= limit:
        return value
    return value[:limit] + "..."


async def _call_telegram_order_api(
    product: Any, ptype: str, quantity: int, extra_info: str | None,
) -> dict[str, Any]:
    api_url = str(getattr(config, "API_URL", "") or "").strip()
    api_key = str(getattr(config, "API_KEY", "") or "").strip()
    if not api_url or not api_key:
        return {"ok": False, "error": "API_URL یا API_KEY داخل config تنظیم نشده است"}

    service_id = _telegram_api_service_id(ptype, product)
    if not service_id:
        return {"ok": False, "error": "service_id برای این محصول تشخیص داده نشد"}

    try:
        order_link = _telegram_api_order_link(extra_info)
    except Exception as e:
        return {"ok": False, "error": str(e)}

    api_quantity = 1 if ptype == PRODUCT_TYPE_TELEGRAM_PREMIUM else int(quantity)
    params = {
        "key": api_key,
        "action": "add",
        "quantity": api_quantity,
        "service": service_id,
        "link": order_link,
    }
    request_url = _build_api_get_url(api_url, params)
    timeout_seconds = int(getattr(config, "API_TIMEOUT", 25) or 25)

    try:
        timeout = aiohttp.ClientTimeout(total=timeout_seconds)
        async with aiohttp.ClientSession(timeout=timeout, headers={"User-Agent": "Reinbot/2"}) as session:
            async with session.get(request_url) as resp:
                raw = await resp.text()
                try:
                    data = await resp.json(content_type=None)
                except Exception:
                    data = None
                if 200 <= resp.status < 300 and isinstance(data, dict) and data.get("order"):
                    return {
                        "ok": True,
                        "order": str(data.get("order")),
                        "status": resp.status,
                        "raw": raw,
                        "service": service_id,
                        "quantity": api_quantity,
                        "link": order_link,
                        "url": request_url,
                    }
                return {
                    "ok": False,
                    "error": "خروجی API شامل order معتبر نبود",
                    "status": resp.status,
                    "raw": raw,
                    "data": data,
                    "service": service_id,
                    "quantity": api_quantity,
                    "link": order_link,
                    "url": request_url,
                }
    except Exception as e:
        return {
            "ok": False,
            "error": f"خطا در اتصال به API: {e}",
            "service": service_id,
            "quantity": api_quantity,
            "link": order_link,
            "url": request_url,
        }


async def _claim_referral_bonus_after_auto_delivery(ctx: ContextTypes.DEFAULT_TYPE, user_id: int) -> None:
    referrer_id = db_mod.claim_referrer_bonus_for_referee(user_id)
    if referrer_id:
        db_mod.update_balance(referrer_id, config.REFERRAL_BONUS)
        try:
            await ctx.bot.send_message(
                referrer_id,
                f"🎉 <b>پاداش زیرمجموعه دریافت شد!</b>\n\n"
                f"💰 <b>{_fmt_balance(config.REFERRAL_BONUS)}</b> به موجودی شما اضافه شد.",
                parse_mode=ParseMode.HTML,
            )
        except Exception:
            pass


async def _handle_telegram_api_order(
    ctx: ContextTypes.DEFAULT_TYPE, order_id: int, user_id: int, product: Any,
    ptype: str, quantity: int, extra_info: str | None,
) -> dict[str, Any]:
    result = await _call_telegram_order_api(product, ptype, quantity, extra_info)
    urow = db_mod.get_user(user_id)
    full_name = _row_get(urow, "full_name", str(user_id)) if urow else str(user_id)
    username = _row_get(urow, "username", None) if urow else None
    product_name = _row_get(product, "name", "محصول")
    qty_line = _qty_line_for_order(ptype, int(result.get("quantity") or quantity))
    service_txt = result.get("service", "—")
    try:
        link_txt = result.get("link") or _telegram_api_order_link(extra_info)
    except Exception:
        link_txt = extra_info or "—"

    if result.get("ok"):
        api_order_id = result.get("order")
        delivery_note = (
            f"[API_ORDER: {api_order_id}] [SERVICE: {service_txt}] "
            f"[تعداد: {int(result.get('quantity') or quantity)}] [لینک: {link_txt}]"
        )
        db_mod.update_order_status(order_id, "delivered", delivery_note)
        await _claim_referral_bonus_after_auto_delivery(ctx, user_id)
        admin_text = (
            "✅ <b>سفارش داخل API ثبت شد</b>\n\n"
            f"👤 نام: <b>{_he(full_name)}</b>\n"
            f"🆔 شناسه: <code>{user_id}</code>\n"
            f"📛 یوزرنیم: @{_he(username) if username else '—'}\n\n"
            f"📦 محصول: <b>{_he(product_name)}</b>\n"
            f"{qty_line}"
            f"🔗 لینک سفارش: <code>{_he(link_txt)}</code>\n"
            f"🧩 Service ID: <code>{_he(service_txt)}</code>\n"
            f"🔖 سفارش ربات: <b>#{order_id}</b>\n"
            f"🧾 سفارش API: <code>{_he(api_order_id)}</code>"
        )
        await _notify_admins(ctx, "notify_order", admin_text)
        return result

    raw = result.get("raw") if result.get("raw") is not None else result.get("data")
    err_text = result.get("error", "خطای نامشخص")
    admin_text = (
        "⚠️ <b>خطا در ثبت سفارش داخل API</b>\n\n"
        f"👤 نام: <b>{_he(full_name)}</b>\n"
        f"🆔 شناسه: <code>{user_id}</code>\n"
        f"📛 یوزرنیم: @{_he(username) if username else '—'}\n\n"
        f"📦 محصول: <b>{_he(product_name)}</b>\n"
        f"{qty_line}"
        f"🔗 لینک سفارش: <code>{_he(link_txt)}</code>\n"
        f"🧩 Service ID: <code>{_he(service_txt)}</code>\n"
        f"🔖 سفارش ربات: <b>#{order_id}</b>\n"
        f"📡 HTTP Status: <code>{_he(result.get('status', '—'))}</code>\n"
        f"❌ خطا: <code>{_he(err_text)}</code>\n\n"
        f"🧾 خروجی API:\n<code>{_he(_clip_api_text(raw))}</code>"
    )
    await _notify_admins(ctx, "notify_order", admin_text, _order_admin_kb(order_id))
    return result


def _is_primary_admin(user_id: int) -> bool:
    return user_id in config.ADMIN_IDS


_usd_rate_cache: dict = {"value": 0, "ts": 0.0}
_USD_CACHE_TTL = 60


async def _try_nobitex(session: aiohttp.ClientSession) -> int | None:
    url = getattr(config, "NOBITEX_USDTIRT_ORDERBOOK", "https://api.nobitex.ir/v2/orderbook/USDTIRT")
    try:
        async with session.get(url) as r:
            if r.status != 200:
                return None
            data = await r.json(content_type=None)
            bids = data.get("bids", [])
            asks = data.get("asks", [])
            price_rial = 0.0
            if bids and len(bids[0]) >= 1:
                price_rial = float(str(bids[0][0]).replace(",", ""))
            elif asks and len(asks[0]) >= 1:
                price_rial = float(str(asks[0][0]).replace(",", ""))
            if price_rial > 100_000:
                return int(price_rial / 10)
    except Exception as e:
        log.warning("[نرخ] Nobitex: %s", e)
    return None


async def _try_wallex(session: aiohttp.ClientSession) -> int | None:
    url = getattr(config, "WALLEX_MARKETS_URL", "https://api.wallex.ir/v1/markets")
    try:
        async with session.get(url) as r:
            if r.status != 200:
                return None
            data = await r.json(content_type=None)
            usdt = ((data.get("result") or {}).get("symbols") or {}).get("USDTTMN") or {}
            stats = usdt.get("stats") or {}
            raw = stats.get("bidPrice") or stats.get("lastPrice") or stats.get("askPrice")
            if raw is None:
                return None
            val = int(float(str(raw).replace(",", "")))
            if val > 10_000:
                return val
    except Exception as e:
        log.warning("[نرخ] Wallex: %s", e)
    return None


async def _fetch_usd_to_irt() -> int:
    import time
    now = time.monotonic()
    if _usd_rate_cache["value"] > 0 and (now - _usd_rate_cache["ts"]) < _USD_CACHE_TTL:
        return _usd_rate_cache["value"]

    def _set(val: int, src: str) -> int:
        _usd_rate_cache["value"] = val
        _usd_rate_cache["ts"] = time.monotonic()
        log.info("[نرخ] %s → %s", src, f"{val:,}")
        return val

    try:
        async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=8),
                                         headers={"User-Agent": "Reinbot/2"}) as s:
            n = await _try_nobitex(s)
            if n and n > 10_000:
                return _set(n, "Nobitex")
            w = await _try_wallex(s)
            if w and w > 10_000:
                return _set(w, "Wallex")
    except Exception as e:
        log.warning("[نرخ] خطا: %s", e)

    fallback = _usd_rate_cache["value"] if _usd_rate_cache["value"] > 0 else config.FALLBACK_USD_TO_IRT
    return fallback


async def _fetch_crypto_price_usd(symbol: str) -> tuple[float, float]:
    cg_ids: dict[str, str] = {
        "usdt":  "tether",
        "trx":   "tron",
        "ton":   "the-open-network",
        "btc":   "bitcoin",
        "eth":   "ethereum",
        "bnb":   "binancecoin",
        "sol":   "solana",
        "xrp":   "ripple",
        "ada":   "cardano",
        "doge":  "dogecoin",
        "dot":   "polkadot",
        "shib":  "shiba-inu",
        "avax":  "avalanche-2",
        "matic": "matic-network",
        "pol":   "matic-network",
        "link":  "chainlink",
        "ltc":   "litecoin",
        "bch":   "bitcoin-cash",
        "xlm":   "stellar",
        "atom":  "cosmos",
        "near":  "near",
        "ftm":   "fantom",
        "algo":  "algorand",
        "icp":   "internet-computer",
        "vet":   "vechain",
        "xmr":   "monero",
        "egld":  "elrond-erd-2",
        "sand":  "the-sandbox",
        "mana":  "decentraland",
        "axs":   "axie-infinity",
        "apt":   "aptos",
        "sui":   "sui",
        "arb":   "arbitrum",
        "op":    "optimism",
        "not":   "notcoin",
        "dogs":  "dogs-2",
        "hmstr": "hamster-kombat",
        "usdc":  "usd-coin",
        "busd":  "binance-usd",
        "dai":   "dai",
        "tusd":  "true-usd",
    }
    sym = symbol.lower()
    cg_id = cg_ids.get(sym, sym)
    try:
        async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=8)) as s:
            url = (
                f"https://api.coingecko.com/api/v3/simple/price"
                f"?ids={cg_id}&vs_currencies=usd&include_24hr_change=true"
            )
            async with s.get(url) as r:
                if r.status == 200:
                    data = await r.json()
                    coin_data = data.get(cg_id, {})
                    price = coin_data.get("usd", 0.0)
                    change = coin_data.get("usd_24h_change", 0.0)
                    return float(price), float(change or 0.0)
    except Exception as e:
        log.warning("[قیمت ارز] خطا برای %s: %s", symbol, e)
    return 0.0, 0.0


_PERSIAN_CRYPTO_ALIASES: dict[str, str] = {
    "تتر": "usdt", "usdt": "usdt", "تدر": "usdt", "دلار": "usdt",
    "ترون": "trx", "trx": "trx",
    "تون": "ton", "تن": "ton", "ton": "ton", "تون کوین": "ton",
    "بیتکوین": "btc", "btc": "btc", "بیت کوین": "btc", "بیت‌کوین": "btc",
    "اتریوم": "eth", "eth": "eth", "اتر": "eth", "ایتریوم": "eth",
    "بایننس": "bnb", "bnb": "bnb", "بی ان بی": "bnb",
    "سولانا": "sol", "sol": "sol",
    "ریپل": "xrp", "xrp": "xrp",
    "کاردانو": "ada", "ada": "ada",
    "دوج": "doge", "doge": "doge", "دوج کوین": "doge", "dogecoin": "doge",
    "پولکادات": "dot", "dot": "dot",
    "شیبا": "shib", "shib": "shib", "شیبا اینو": "shib",
    "اولانچ": "avax", "avax": "avax", "آوالانچ": "avax",
    "ماتیک": "matic", "matic": "matic", "پولیگان": "matic", "pol": "pol",
    "چین لینک": "link", "link": "link", "لینک": "link",
    "لایت کوین": "ltc", "ltc": "ltc",
    "بیتکوین کش": "bch", "bch": "bch",
    "استلار": "xlm", "xlm": "xlm",
    "کازماس": "atom", "atom": "atom", "کاسموس": "atom",
    "نیر": "near", "near": "near",
    "نات کوین": "not", "not": "not", "ناتکوین": "not",
    "داگز": "dogs", "dogs": "dogs",
    "همستر": "hmstr", "hmstr": "hmstr", "هامستر": "hmstr",
    "یو اس دی سی": "usdc", "usdc": "usdc",
    "آربیتروم": "arb", "arb": "arb",
    "اپتیمیزم": "op", "op": "op",
    "اپتوس": "apt", "apt": "apt",
    "سویی": "sui", "sui": "sui",
}

_CRYPTO_DISPLAY_NAMES: dict[str, str] = {
    "usdt": "تتر (USDT)",    "trx":  "ترون (TRX)",     "ton":  "تون (TON)",
    "btc":  "بیتکوین (BTC)", "eth":  "اتریوم (ETH)",   "bnb":  "بایننس (BNB)",
    "sol":  "سولانا (SOL)",  "xrp":  "ریپل (XRP)",     "ada":  "کاردانو (ADA)",
    "doge": "دوج (DOGE)",    "dot":  "پولکادات (DOT)",  "shib": "شیبا (SHIB)",
    "avax": "اولانچ (AVAX)", "matic":"ماتیک (MATIC)",  "link": "چین‌لینک (LINK)",
    "ltc":  "لایت‌کوین (LTC)","bch": "بیت‌کوین کش (BCH)","xlm":"استلار (XLM)",
    "atom": "کازماس (ATOM)", "near": "نیر (NEAR)",      "not":  "نات‌کوین (NOT)",
    "dogs": "داگز (DOGS)",   "hmstr":"همستر (HMSTR)",   "usdc": "یو اس دی سی (USDC)",
    "arb":  "آربیتروم (ARB)","op":   "اپتیمیزم (OP)",  "apt":  "اپتوس (APT)",
    "sui":  "سویی (SUI)",    "pol":  "پولیگان (POL)",
}

_CRYPTO_EMOJIS: dict[str, str] = {
    "usdt": "💵", "trx": "🔴", "ton": "💎", "btc": "₿",  "eth": "🔷",
    "bnb":  "🟡", "sol": "🌅", "xrp": "🔵", "ada": "🔵", "doge": "🐕",
    "dot":  "⚪", "shib":"🐕", "avax":"🔺", "matic":"🟣","link": "🔗",
    "ltc":  "⚡", "bch": "💚", "xlm": "⭐", "atom":"⚛️", "near":"🟩",
    "not":  "🚫", "dogs":"🐶", "hmstr":"🐹","usdc":"💲", "arb": "🔵",
    "op":   "🔴", "apt": "🔷", "sui": "🌊", "pol": "🟣",
}


def _get_cat_emoji_id(cat_name: str, cat_emoji: str) -> str | None:
    name_low = cat_name.lower()
    if any(k in name_low for k in ("پریمیوم", "premium", "پریم")):
        return EMOJI_PREMIUM
    if any(k in name_low for k in ("استارز", "stars", "star")):
        return EMOJI_STARS
    if any(k in name_low for k in ("بوست", "boost")):
        return EMOJI_BOOST
    if any(k in name_low for k in ("chatgpt", "چت جی پی تی", "gpt", "openai", "چت جی پی تی پلاس")):
        return EMOJI_CHATGPT
    if any(k in name_low for k in ("gemini", "جمینای", "بارد", "جمینای", "جمنای", "جمنای پرو")):
        return EMOJI_GEMINI
    if any(k in name_low for k in ("هوش مصنوعی", "هوش‌مصنوعی", "ai", "artificial intelligence")):
        return EMOJI_AI
    if any(k in name_low for k in ("فرگمنت", "fragment")):
        return EMOJI_FRAGMENT
    return None


def _parse_group_crypto_query(text: str) -> tuple[str | None, float | None]:
    fa_map = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")
    text_norm = text.translate(fa_map).strip().lower()

    aliases_sorted = sorted(_PERSIAN_CRYPTO_ALIASES.keys(), key=len, reverse=True)
    aliases_pattern = "|".join(re.escape(a.lower()) for a in aliases_sorted)
    pattern = rf"([\d]+(?:[.,]\d+)?)\s*({aliases_pattern})|({aliases_pattern})\s*([\d]+(?:[.,]\d+)?)"
    m = re.search(pattern, text_norm)
    if not m:
        return None, None

    if m.group(1) and m.group(2):
        amount_str, coin_str = m.group(1), m.group(2)
    else:
        coin_str, amount_str = m.group(3), m.group(4)

    amount_str = amount_str.replace(",", ".")
    try:
        amount = float(amount_str)
    except ValueError:
        return None, None

    symbol = _PERSIAN_CRYPTO_ALIASES.get(coin_str)
    if not symbol:
        return None, None

    return symbol, amount




_GROUP_PRODUCT_KEYWORDS: dict[str, str] = {
    "پریم":              PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "پریمیوم":           PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "پرمیوم":            PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "premi":             PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "premium":           PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "تلگرام پریمیوم":   PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "پریمیوم تلگرام":   PRODUCT_TYPE_TELEGRAM_PREMIUM,
    "استارز":            PRODUCT_TYPE_TELEGRAM_STARS,
    "stars":             PRODUCT_TYPE_TELEGRAM_STARS,
    "استار":             PRODUCT_TYPE_TELEGRAM_STARS,
    "تلگرام استارز":    PRODUCT_TYPE_TELEGRAM_STARS,
    "استارز تلگرام":    PRODUCT_TYPE_TELEGRAM_STARS,
    "چت جی پی تی":      PRODUCT_TYPE_CHATGPT,
    "chatgpt":           PRODUCT_TYPE_CHATGPT,
    "chat gpt":          PRODUCT_TYPE_CHATGPT,
    "چتgpt":             PRODUCT_TYPE_CHATGPT,
    "gpt":               PRODUCT_TYPE_CHATGPT,
    "جی پی تی":         PRODUCT_TYPE_CHATGPT,
    "openai":            PRODUCT_TYPE_CHATGPT,
    "open ai":           PRODUCT_TYPE_CHATGPT,
    "چت‌جی‌پی‌تی":     PRODUCT_TYPE_CHATGPT,
    "جمینای":            PRODUCT_TYPE_GEMINI,
    "gemini":            PRODUCT_TYPE_GEMINI,
    "گوگل جمینای":      PRODUCT_TYPE_GEMINI,
    "google gemini":     PRODUCT_TYPE_GEMINI,
    "جمینی":             PRODUCT_TYPE_GEMINI,
    "bard":              PRODUCT_TYPE_GEMINI,
    "بوست":              PRODUCT_TYPE_BOOST,
    "boost":             PRODUCT_TYPE_BOOST,
    "بوست کانال":       PRODUCT_TYPE_BOOST,
    "شماره مجازی":       PRODUCT_TYPE_VIRTUAL_NUMBER,
    "شماره موقت":        PRODUCT_TYPE_VIRTUAL_NUMBER,
    "virtual number":    PRODUCT_TYPE_VIRTUAL_NUMBER,
    "شماره":             PRODUCT_TYPE_VIRTUAL_NUMBER,
    "فرگمنت":            PRODUCT_TYPE_FRAGMENT,
    "fragment":          PRODUCT_TYPE_FRAGMENT,
}

_PRODUCT_TYPE_LABELS: dict[str, str] = {
    PRODUCT_TYPE_TELEGRAM_PREMIUM: "📱 تلگرام پریمیوم",
    PRODUCT_TYPE_TELEGRAM_STARS:   "⭐ استارز تلگرام",
    PRODUCT_TYPE_CHATGPT:          "🤖 ChatGPT",
    PRODUCT_TYPE_GEMINI:           "🔷 Gemini",
    PRODUCT_TYPE_VIRTUAL_NUMBER:   "📞 شماره مجازی",
    PRODUCT_TYPE_BOOST:            "🚀 بوست کانال",
    PRODUCT_TYPE_FRAGMENT:         "💎 فرگمنت",
    PRODUCT_TYPE_GENERIC:          "📦 محصول",
}


def _match_group_product_query(text: str) -> str | None:
    t = text.strip().lower()
    for kw in sorted(_GROUP_PRODUCT_KEYWORDS, key=len, reverse=True):
        if t == kw or t.startswith(kw + " ") or t.endswith(" " + kw) or f" {kw} " in t:
            return _GROUP_PRODUCT_KEYWORDS[kw]
    return None


async def _build_group_product_reply(ptype: str, usd_irt: int) -> str:
    label = _PRODUCT_TYPE_LABELS.get(ptype, "📦 محصول")
    cats = db_mod.get_categories()
    found_products = []

    for cat in cats:
        products = db_mod.get_products_by_category(cat["cat_id"])
        for p in products:
            if _detect_product_type(p["name"], p["description"] if p["description"] else "") == ptype:
                found_products.append(p)

    jdt = _jalali_dt_now()
    date_str = f"{jdt.year}/{jdt.month:02d}/{jdt.day:02d} | {jdt.hour:02d}:{jdt.minute:02d}:{jdt.second:02d}"

    if not found_products:
        return (
            f"{label}\n\n"
            f"❌ <b>این محصول در فروشگاه موجود نیست.</b>\n\n"
            f"🕒 {date_str}"
        )

    lines = [f"{label} — <b>لیست قیمت</b>", ""]
    lines.append(f"💱 نرخ دلار: <code>{usd_irt:,} تومان</code>")
    lines.append("━━━━━━━━━━━━━━━━")

    for p in found_products:
        price_irt = int(p["price_usd"] * usd_irt)
        ptype_detected = _detect_product_type(p["name"], p["description"] if p["description"] else "")
        _, fee, _ = _calculate_price_with_fee(price_irt, ptype_detected)
        total_irt = price_irt + fee

        if p["stock"] == 0:
            stock_icon = "❌ ناموجود"
        elif p["stock"] == -1:
            stock_icon = "✅ موجود"
        else:
            stock_icon = f"✅ موجود ({p['stock']} عدد)"

        lines.append(
            f"\n🔹 <b>{p['name']}</b>\n"
            f"   💵 ${p['price_usd']:.2f}  🇮🇷 <code>{total_irt:,} تومن</code>\n"
            f"   📦 {stock_icon}"
        )

    lines.append("")
    lines.append("━━━━━━━━━━━━━━━━")
    lines.append(f"🕒 {date_str}")
    return "\n".join(lines)


def _parse_irt_to_crypto_query(text: str) -> tuple[str | None, int | None]:
    fa_map = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")
    text_norm = text.translate(fa_map).strip().lower()

    toman_pattern = r"تومان|تومن|irt|irr|tooman|تومن"
    text_no_toman = re.sub(toman_pattern, " ", text_norm).strip()

    num_match = re.search(r"([\d]+(?:[.,]\d+)?)", text_norm)
    if not num_match:
        return None, None

    if not re.search(toman_pattern, text_norm):
        return None, None

    amount_str = num_match.group(1).replace(",", "")
    try:
        amount_irt = int(float(amount_str))
    except ValueError:
        return None, None

    aliases_sorted = sorted(_PERSIAN_CRYPTO_ALIASES.keys(), key=len, reverse=True)
    for alias in aliases_sorted:
        if alias.lower() in text_no_toman:
            return _PERSIAN_CRYPTO_ALIASES[alias], amount_irt

    return None, None


async def handle_group_price(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    msg = update.message
    if not msg or not msg.text:
        return

    text = msg.text.strip()

    ptype = _match_group_product_query(text)
    if ptype:
        usd_irt = await _fetch_usd_to_irt()
        reply = await _build_group_product_reply(ptype, usd_irt)
        await msg.reply_text(reply, parse_mode=ParseMode.HTML)
        return

    symbol_rev, amount_irt = _parse_irt_to_crypto_query(text)
    if symbol_rev and amount_irt:
        try:
            price_usd, change_24h = await _fetch_crypto_price_usd(symbol_rev)
        except Exception:
            return
        if price_usd > 0:
            usd_irt = await _fetch_usd_to_irt()
            crypto_amount = amount_irt / (price_usd * usd_irt)
            coin_name  = _CRYPTO_DISPLAY_NAMES.get(symbol_rev, symbol_rev.upper())
            jdt = _jalali_dt_now()
            date_str = f"{jdt.year}/{jdt.month:02d}/{jdt.day:02d} | {jdt.hour:02d}:{jdt.minute:02d}:{jdt.second:02d}"
            change_sign = "📈" if change_24h >= 0 else "📉"
            change_color = "+" if change_24h >= 0 else ""
            reply_text = (
                f"🇮🇷 <code>{amount_irt:,} تومن</code>\n"
                f"💱 <b>{crypto_amount:.6f} {coin_name}</b>\n"
                f"💵 قیمت واحد: <b>${price_usd:,.4f}</b>\n"
                f"{change_sign} تغییرات ۲۴h: <b>{change_color}{change_24h:.2f}%</b>\n"
                f"🕒 {date_str}\n"
                f"━━━━━━━━━━━━━━━━\n"
            )
            await msg.reply_text(reply_text, parse_mode=ParseMode.HTML)
            return

    symbol, amount = _parse_group_crypto_query(text)
    if not symbol or not amount:
        return

    try:
        price_usd, change_24h = await _fetch_crypto_price_usd(symbol)
    except Exception:
        return

    if price_usd <= 0:
        await msg.reply_text(
            f"⚠️ قیمت <b>{symbol.upper()}</b> در دسترس نیست.",
            parse_mode=ParseMode.HTML,
        )
        return

    usd_irt = await _fetch_usd_to_irt()
    total_usd = price_usd * amount
    total_irt = int(total_usd * usd_irt)

    coin_name  = _CRYPTO_DISPLAY_NAMES.get(symbol, symbol.upper())
    coin_emoji = _CRYPTO_EMOJIS.get(symbol, "🪙")
    change_sign  = "📈" if change_24h >= 0 else "📉"
    change_color = "+" if change_24h >= 0 else ""

    if amount == int(amount):
        amount_str = f"{int(amount):,}"
    else:
        amount_str = f"{amount:,.4f}".rstrip("0").rstrip(".")

    if price_usd >= 1:
        price_str = f"${price_usd:,.2f}"
    elif price_usd >= 0.001:
        price_str = f"${price_usd:.4f}"
    else:
        price_str = f"${price_usd:.8f}"

    jdt = _jalali_dt_now()
    date_str = f"{jdt.year}/{jdt.month:02d}/{jdt.day:02d} | {jdt.hour:02d}:{jdt.minute:02d}:{jdt.second:02d}"

    reply_text = (
        f"{coin_emoji} <b>{amount_str} {coin_name}</b> = <b>{price_str}</b>\n"
        f"🇮🇷  <code>{total_irt:,} تومن</code>\n"
        f"🕒 {date_str}\n"
        f"━━━━━━━━━━━━━━━━\n"
        f"{change_sign} تغییرات ۲۴h: <b>{change_color}{change_24h:.2f}%</b>\n"
    )
    await msg.reply_text(reply_text, parse_mode=ParseMode.HTML)


async def handle_bot_added_to_group(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    msg = update.message
    if not msg:
        return
    new_members = msg.new_chat_members or []
    bot_self = await ctx.bot.get_me()
    bot_added = any(m.id == bot_self.id for m in new_members)
    if not bot_added:
        return

    intro = (
        "👋 <b>سلام! من ربات فروشگاه هستم.</b>\n\n"
        "می‌توانم در این گروه:\n\n"
        "🏷 <b>قیمت محصولات فروشگاه را نمایش دهم:</b>\n"
        "  فقط بنویسید: <code>پریمیوم</code> یا <code>استارز</code>\n"
        "  یا <code>چت جی پی تی</code> یا <code>بوست</code>\n\n"
        "💱 <b>ارز دیجیتال را به تومان تبدیل کنم:</b>\n"
        "  مثال: <code>تتر ۱۰۰</code> یا <code>۰.۵ بیتکوین</code>\n\n"
        "🛒 برای خرید مستقیم با ربات در پیوی تماس بگیرید."
    )
    try:
        await msg.reply_text(intro, parse_mode=ParseMode.HTML)
    except Exception as e:
        log.warning("[گروه] ارسال پیام معرفی ناموفق: %s", e)




def _fmt_balance(amount: int) -> str:
    return f"{amount:,} تومان"


def _get_kyc_threshold() -> int:
    val = db_mod.get_setting("kyc_threshold")
    try:
        return int(val) if val else config.KYC_THRESHOLD
    except Exception:
        return config.KYC_THRESHOLD


def _get_premium_fee() -> int:
    val = db_mod.get_setting("premium_fee")
    try:
        return int(val) if val else config.PREMIUM_FEE_IRT
    except Exception:
        return config.PREMIUM_FEE_IRT


def _get_stars_fee_pct() -> float:
    val = db_mod.get_setting("stars_fee_pct")
    try:
        return float(val) if val else config.STARS_FEE_PERCENT
    except Exception:
        return config.STARS_FEE_PERCENT


def _get_general_fee_percent() -> float:
    val = db_mod.get_setting("fee_percent")
    try:
        return float(val) if val else 0.0
    except Exception:
        return 0.0


def _calculate_price_with_fee(base_price_irt: int, ptype: str) -> tuple[int, int, str]:
    if ptype == PRODUCT_TYPE_TELEGRAM_PREMIUM:
        fee = _get_premium_fee()
        return base_price_irt, fee, f"کارمزد ثابت پریمیوم: {_fmt_balance(fee)}"
    elif ptype == PRODUCT_TYPE_TELEGRAM_STARS:
        pct = _get_stars_fee_pct()
        fee = int(base_price_irt * pct / 100)
        return base_price_irt, fee, f"کارمزد استارز ({pct:.0f}٪): {_fmt_balance(fee)}"
    else:
        pct = _get_general_fee_percent()
        if pct > 0:
            fee = int(base_price_irt * pct / 100)
            return base_price_irt, fee, f"کارمزد ({pct:.0f}٪): {_fmt_balance(fee)}"
        return base_price_irt, 0, ""


def _level_info(user: Any) -> dict:
    spent = user["total_spent"]
    current_lvl = user["level"]
    info = config.LEVEL_THRESHOLDS.get(current_lvl, config.LEVEL_THRESHOLDS[0])
    next_info = config.LEVEL_THRESHOLDS.get(current_lvl + 1)
    remaining = (next_info["min"] - spent) if next_info else 0
    return {"level": current_lvl, "title": info["title"], "emoji": info["emoji"],
            "remaining": max(0, remaining)}


def _build_extra_line(ptype: str, extra_info: str | None) -> str:
    if not extra_info:
        return ""
    mapping = {
        PRODUCT_TYPE_TELEGRAM_PREMIUM: f"📱 یوزرنیم تلگرام: <code>{extra_info}</code>\n",
        PRODUCT_TYPE_TELEGRAM_STARS:   f"⭐ یوزرنیم تلگرام: <code>{extra_info}</code>\n",
        PRODUCT_TYPE_CHATGPT:          f"📧 ایمیل: <code>{extra_info}</code>\n",
        PRODUCT_TYPE_VIRTUAL_NUMBER:   f"📞 درخواست: <code>{extra_info}</code>\n",
        PRODUCT_TYPE_BOOST:            f"🚀 آیدی کانال/گروه: <code>{extra_info}</code>\n",
    }
    return mapping.get(ptype, "")

async def _check_banned(update: Update) -> bool:
    user_id = update.effective_user.id if update.effective_user else None
    if not user_id:
        return False
    row = db_mod.get_user(user_id)
    if row and row["is_banned"]:
        if update.callback_query:
            await update.callback_query.answer("🚫 دسترسی شما مسدود است", show_alert=True)
        elif update.message:
            await update.message.reply_text(
                "🚫 <b>دسترسی شما به ربات مسدود شده است.</b>\n\nبرای پیگیری با پشتیبانی تماس بگیرید.",
                parse_mode=ParseMode.HTML)
        return True
    return False


async def _section_closed_msg(update: Update, section_name: str) -> None:
    msg = (f"🔒 <b>بخش {section_name} موقتاً بسته است.</b>\n\n"
           "این بخش توسط مدیر غیرفعال شده است.\nلطفاً بعداً مراجعه کنید.")
    kb = _kb([_btn("بازگشت به منو", cb="back_main", emoji_id="5346112927688574301", style="danger")])
    if update.callback_query:
        await update.callback_query.answer()
        await update.callback_query.edit_message_text(msg, reply_markup=kb, parse_mode=ParseMode.HTML)
    elif update.message:
        await update.message.reply_text(msg, reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)


async def _check_forced_join(bot: Bot, user_id: int) -> bool:
    channel = db_mod.get_setting("forced_channel") or config.FORCED_JOIN_CHANNEL
    if not channel:
        return True
    try:
        member = await bot.get_chat_member(channel, user_id)
        return member.status not in ("left", "kicked")
    except Exception:
        return True


async def _send_join_required(update: Update, channel: str) -> None:
    kb = _kb(
        [_btn("📢 عضویت در کانال", url=f"https://t.me/{channel.lstrip('@')}")],
        [_btn("✅ عضو شدم — بررسی کن", cb="check_join")],
    )
    text = (f"⛔️ <b>دسترسی محدود شده</b>\n\n"
            f"برای استفاده از ربات ابتدا باید در کانال ما عضو شوید.\n\n"
            f"📢 کانال: <b>{channel}</b>")
    if update.callback_query:
        await update.callback_query.answer()
        await update.callback_query.message.reply_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)
    elif update.message:
        await update.message.reply_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)

CRYPTO_NETWORK_INFO = {
    "usdt": {"networks": ["TRC20 (Tron)", "ERC20 (Ethereum)", "BEP20 (BSC)"],
             "recommended": "TRC20 (Tron)",
             "warning": "⚠️ حتماً شبکه انتخابی را هنگام واریز انتخاب کنید. واریز با شبکه اشتباه منجر به از دست رفتن دارایی می‌شود!"},
    "trx":  {"networks": ["TRC20 (Tron)"], "recommended": "TRC20 (Tron)",
             "warning": "⚠️ فقط از شبکه Tron (TRC20) استفاده کنید."},
    "ton":  {"networks": ["TON Mainnet"], "recommended": "TON Mainnet",
             "warning": "⚠️ فقط از شبکه اصلی TON واریز کنید. در صورت وجود memo/comment آن را حتماً وارد کنید."},
    "btc":  {"networks": ["Bitcoin Mainnet (BTC)"], "recommended": "Bitcoin Mainnet (BTC)",
             "warning": "⚠️ فقط از شبکه اصلی بیتکوین واریز کنید."},
}


def _resolved_deposit_network(crypto_key: str) -> tuple[list[str], str]:
    net_info = CRYPTO_NETWORK_INFO.get(crypto_key, {})
    networks: list[str] = list(net_info.get("networks", []))
    default_rec = net_info.get("recommended", "") or (networks[0] if networks else "")
    raw = (db_mod.get_setting(f"deposit_network_{crypto_key}") or "").strip()
    if raw and raw in networks:
        return networks, raw
    if default_rec in networks:
        return networks, default_rec
    return networks, (networks[0] if networks else default_rec)

MAIN_MENU_KB = _kb(
    [_btn("منوی محصولات", cb="menu_products", emoji_id="5348183286608840968", style="none"),
     _btn("حساب کاربری",  cb="menu_account",  emoji_id="5346136537123801643", style="none")],
    [_btn("افزایش موجودی",    cb="menu_topup",    emoji_id="5346227465876423936", style="none"),
     _btn("زیرمجموعه‌گیری",  cb="menu_referral", emoji_id="5348220115953403873", style="none")],
    [_btn("تاریخچه سفارشات", cb="menu_orders",   emoji_id="5348178055338671586", style="none"),
     _btn("پیگیری سفارش",   cb="menu_track",    emoji_id="5345840270279724328", style="none")],
    [_btn("افزودن ربات به گروه", cb="add_to_group", emoji_id="5994442901059276913", style="none")],
    [_btn("پشتیبانی", cb="menu_support", emoji_id="5348348681504441752", style="none")],
)


async def cmd_start(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    user = update.effective_user
    if await _check_banned(update):
        return

    ctx.user_data.clear()

    args = ctx.args
    referrer_id = None
    if args and args[0].isdigit():
        ref = int(args[0])
        if ref != user.id:
            referrer_id = ref

    is_new = db_mod.get_user(user.id) is None
    db_mod.upsert_user(user.id, user.username, user.full_name, referrer_id)

    if is_new:
        uname = f"@{user.username}" if user.username else "ندارد"
        total = db_mod.get_users_count()
        notif = (
            "👤 <b>کاربر جدید ثبت‌نام کرد!</b>\n\n"
            f"🆔 شناسه: <code>{user.id}</code>\n"
            f"👤 نام: <b>{user.full_name}</b>\n"
            f"📛 یوزرنیم: {uname}\n"
            f"🔗 معرف: {referrer_id if referrer_id else 'مستقیم'}\n"
            f"👥 کل کاربران: <b>{total}</b>\n\n"
            f"🕐 {_jalali_now()}"
        )
        await _notify_admins(
            ctx, "notify_registration", notif,
            _kb([_btn("💬 ارسال پیام به کاربر", cb=f"adm_dm_{user.id}")]),
        )

    channel = db_mod.get_setting("forced_channel") or config.FORCED_JOIN_CHANNEL
    if channel and not await _check_forced_join(ctx.bot, user.id):
        await _send_join_required(update, channel)
        return

    text = f"سلام <b>{user.first_name}</b> عزیز! 👋\n\nاز منوی زیر گزینه مورد نظر را انتخاب کنید:"
    if update.message:
        await update.message.reply_text(text, reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
        await _react(ctx.bot, update.message.chat_id, update.message.message_id, "🎉")
    else:
        await update.callback_query.edit_message_text(text, reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)


async def cb_account(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if await _check_banned(update):
        return
    user_id = q.from_user.id
    row = db_mod.get_user(user_id)
    if not row:
        return
    lvl = _level_info(row)
    ref_count = db_mod.get_referral_count(user_id)
    paid_count = sum(1 for o in db_mod.get_user_orders(user_id) if o["status"] == "delivered")
    uname = f"@{q.from_user.username}" if q.from_user.username else "—"
    next_lvl_info = config.LEVEL_THRESHOLDS.get(lvl["level"] + 1)
    remaining_txt = _fmt_balance(lvl["remaining"]) if next_lvl_info else "🏆 حداکثر سطح"
    verified_txt = "✅ تایید شده" if row["is_verified"] else "❌ تایید نشده"

    text = (
        "📊 <b>حساب کاربری شما</b>\n\n"
        f"🆔 شناسه: <code>{user_id}</code>\n"
        f"👤 یوزرنیم: {uname}\n"
        f"🔐 احراز هویت: {verified_txt}\n\n"
        f"💰 موجودی کیف پول: <b>{_fmt_balance(row['balance'])}</b>\n"
        f"🛒 خریدهای موفق: <b>{paid_count} سفارش</b>\n"
        f"📈 کل خرید: <b>{_fmt_balance(row['total_spent'])}</b>\n\n"
        f"{lvl['emoji']} سطح: <b>{lvl['title']}</b> (سطح {lvl['level']})\n"
        f"⏫ تا سطح بعدی: <b>{remaining_txt}</b>\n\n"
        f"👥 زیرمجموعه‌ها: <b>{ref_count} نفر</b>\n\n"
        f"🕐 <i>{_jalali_now()}</i>"
    )

    pending_kyc = db_mod.get_pending_kyc_by_user(user_id)
    if row["is_verified"]:
        kyc_btn = _btn("احراز هویت شده", cb="kyc_request", emoji_id="5348389475103816844")
    elif pending_kyc:
        kyc_btn = _btn("⏳ احراز هویت در انتظار", cb="kyc_request")
    else:
        kyc_btn = _btn("احراز هویت", cb="kyc_request", emoji_id="5301096984617166561")

    kb = _kb(
        [kyc_btn],
        [_btn("🔗 لینک معرفی من", cb="my_referral_link", emoji_id="5348498159251244132"),
         _btn("🎟 ثبت کد تخفیف", cb="user_discount_code")],
        [_btn("بازگشت به منو", cb="back_main", emoji_id="5346112927688574301", style="danger")],
    )
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_my_referral_link(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    bot_me = await ctx.bot.get_me()
    link = f"https://t.me/{bot_me.username}?start={q.from_user.id}"
    text = (
        "🔗 <b>لینک معرفی اختصاصی شما</b>\n\n"
        f"<code>{link}</code>\n\n"
        f"به ازای هر دوستی که با این لینک ثبت‌نام کند، "
        f"<b>{_fmt_balance(config.REFERRAL_BONUS)}</b> به حساب شما اضافه می‌شود."
    )
    kb = _kb([_copy_btn("📋 کپی لینک معرفی", link)], [_btn("بازگشت", cb="menu_account", emoji_id="5346112927688574301")])
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_user_discount_code(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    ctx.user_data["state"] = "awaiting_user_discount_code"
    await q.edit_message_text(
        "🎟 <b>ثبت کد تخفیف</b>\n\n"
        "کد تخفیف خود را وارد کنید:\n"
        "مثال: <code>SUMMER20</code>\n\n"
        "💡 کد تخفیف هنگام خرید محصول اعمال می‌شود و تا پایان همین جلسه فعال است.",
        reply_markup=_kb([_btn("انصراف", cb="menu_account", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_user_discount_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_user_discount_code":
        return
    ctx.user_data["state"] = None

    code = update.message.text.strip().upper()
    if not code:
        await update.message.reply_text("❌ کد خالی است.", parse_mode=ParseMode.HTML)
        return

    discount_sample, err = db_mod.validate_discount(code, update.effective_user.id, 100_000)
    if err:
        await update.message.reply_text(
            f"{err}\n\nکد دیگری داری؟",
            reply_markup=_kb(
                [_btn("🎟 امتحان مجدد", cb="user_discount_code")],
                [_btn("بازگشت به حساب", cb="menu_account", emoji_id="5346112927688574301")],
            ),
            parse_mode=ParseMode.HTML,
        )
        return

    ctx.user_data["saved_discount_code"] = code
    await update.message.reply_text(
        f"✅ <b>کد تخفیف ذخیره شد!</b>\n\n"
        f"🎟 کد: <code>{code}</code>\n\n"
        f"این کد هنگام بعدی خرید به‌صورت خودکار اعمال می‌شود.\n"
        f"برای استفاده به منوی محصولات بروید و خرید را انجام دهید.",
        reply_markup=_kb(
            [_btn("🛍 منوی محصولات", cb="menu_products")],
            [_btn("بازگشت به حساب", cb="menu_account", emoji_id="5346112927688574301", style="danger")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def cb_add_to_group(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    bot_me = await ctx.bot.get_me()
    add_url = (
        f"https://t.me/{bot_me.username}?startgroup=true"
        f"&admin=post_messages+delete_messages+restrict_members"
    )
    text = (
        "🤖 <b>افزودن ربات به گروه</b>\n\n"
        "با افزودن این ربات به گروه خود، اعضا می‌توانند:\n\n"
        "🏷 <b>مشاهده قیمت محصولات فروشگاه:</b>\n"
        "  • <code>پریمیوم</code> — لیست قیمت تلگرام پریمیوم\n"
        "  • <code>استارز</code> — لیست قیمت استارز تلگرام\n"
        "  • <code>چت جی پی تی</code> — قیمت اکانت ChatGPT\n"
        "  • <code>بوست</code> — لیست قیمت بوست کانال\n\n"
        "💱 <b>تبدیل لحظه‌ای ارز دیجیتال به تومان:</b>\n"
        "  • <code>تتر ۱۰۰</code>\n"
        "  • <code>۰.۵ بیتکوین</code>\n"
        "  • <code>ton 50</code>\n\n"
        "📊 ربات نمایش می‌دهد:\n"
        "  ▫️ قیمت لحظه‌ای دلاری و تومانی\n"
        "  ▫️ تغییرات ۲۴ ساعته\n"
        "  ▫️ لیست محصولات موجود با قیمت\n\n"
        "⚙️ <b>نکته:</b> برای عملکرد بهتر، دسترسی ادمین به ربات بدهید.\n\n"
        "👇 روی دکمه زیر کلیک کنید:"
    )
    kb = _kb(
        [_btn("➕ افزودن به گروه", url=add_url)],
        [_btn("بازگشت به منو", cb="back_main", emoji_id="5346112927688574301", style="danger")],
    )
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)

async def cb_products_menu(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if await _check_banned(update):
        return
    if not db_mod.is_section_active("shop"):
        await _section_closed_msg(update, "فروش")
        return
    cats = db_mod.get_categories()
    if not cats:
        await q.edit_message_text("⚠️ هنوز هیچ دسته‌بندی‌ای ثبت نشده است.",
                                   reply_markup=_kb([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301")]))
        return
    rows = []
    cat_btns = []
    for c in cats:
        eid = _get_cat_emoji_id(c["name"], c["emoji"])
        cat_btns.append(_btn(c["name"], cb=f"cat_{c['cat_id']}",
                             emoji_id=eid, style="primary"))
    for i in range(0, len(cat_btns), 2):
        rows.append(cat_btns[i:i+2])
    rows.append([_btn("بازگشت به منو", cb="back_main", emoji_id="5346112927688574301", style="danger")])
    await q.edit_message_text(
        "🛍 <b>منوی محصولات</b>\n\nدسته‌بندی مورد نظر را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_price_list(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer("⏳ در حال بارگذاری...")
    usd_irt = await _fetch_usd_to_irt()
    cats = db_mod.get_categories()
    has_products = any(db_mod.get_products_by_category(c["cat_id"]) for c in cats)
    if not has_products:
        await q.edit_message_text("⚠️ هیچ محصولی در فروشگاه موجود نیست.",
                                   reply_markup=_kb([_btn("بازگشت", cb="menu_products", emoji_id="5346112927688574301", style="danger")]))
        return
    rows = []
    for cat in cats:
        products = db_mod.get_products_by_category(cat["cat_id"])
        if not products:
            continue
        rows.append([_btn(f"── {cat['emoji']} {cat['name']} ──", cb="noop", style="primary")])
        for p in products:
            price_irt = int(p["price_usd"] * usd_irt)
            rows.append([_btn(f"{p['name']}  |  {price_irt:,} تومان",
                               cb=f"product_{p['product_id']}")])
    rows.append([_btn("بازگشت به دسته‌بندی‌ها", cb="menu_products", emoji_id="5346112927688574301")])
    rows.append([_btn("منوی اصلی", cb="back_main", emoji_id="5346112927688574301", style="danger")])
    await q.edit_message_text(
        f"📋 <b>لیست قیمت</b>  |  💱 دلار: <b>{usd_irt:,} تومان</b>",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_category(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("shop"):
        await _section_closed_msg(update, "فروش")
        return
    cat_id = int(q.data.split("_")[1])
    cat = db_mod.get_category(cat_id)
    products = db_mod.get_products_by_category(cat_id)
    if not products:
        await q.edit_message_text(
            f"📂 دسته <b>{cat['name']}</b>\n\nدر حال حاضر محصولی در این دسته موجود نیست.",
            reply_markup=_kb([_btn("بازگشت به دسته‌بندی‌ها", cb="menu_products", emoji_id="5346112927688574301")]),
            parse_mode=ParseMode.HTML)
        return
    rows = []
    p_btns = []
    for p in products:
        ptype = _detect_product_type(p["name"], p["description"] if p["description"] else "")
        _ptype_emoji_map = {
            PRODUCT_TYPE_TELEGRAM_PREMIUM: EMOJI_PREMIUM,
            PRODUCT_TYPE_TELEGRAM_STARS:   EMOJI_STARS,
            PRODUCT_TYPE_BOOST:            EMOJI_BOOST,
            PRODUCT_TYPE_CHATGPT:          EMOJI_CHATGPT,
            PRODUCT_TYPE_GEMINI:           EMOJI_GEMINI,
            PRODUCT_TYPE_FRAGMENT:         EMOJI_FRAGMENT,
        }
        p_name_low = p["name"].lower()
        if any(k in p_name_low for k in ("فرگمنت", "fragment")):
            p_emoji_id = EMOJI_FRAGMENT
        else:
            p_emoji_id = _ptype_emoji_map.get(ptype)
        p_btns.append(_btn(f"{p['name']}", cb=f"product_{p['product_id']}",
                           emoji_id=p_emoji_id, style="primary"))
    for i in range(0, len(p_btns), 2):
        rows.append(p_btns[i:i+2])
    rows.append([_btn("بازگشت به دسته‌بندی‌ها", cb="menu_products", emoji_id=EMOJI_BACK)])
    await q.edit_message_text(
        f"📂 <b>{cat['name']}</b>\n\nمحصول مورد نظر را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_product_detail(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer("⏳ در حال بارگذاری قیمت‌ها...")
    product_id = int(q.data.split("_")[1])
    p = db_mod.get_product(product_id)
    if not p:
        await q.answer("محصول یافت نشد!", show_alert=True)
        return
    usd_irt = await _fetch_usd_to_irt()
    base_price_irt = int(p["price_usd"] * usd_irt)
    ptype = _detect_product_type(p["name"], p["description"])
    _, fee, fee_desc = _calculate_price_with_fee(base_price_irt, ptype)
    total_price = base_price_irt + fee
    stock_txt = "موجود ✅" if (p["stock"] == -1 or p["stock"] > 0) else "ناموجود ❌"

    is_quantity_product = ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST)

    if is_quantity_product:
        unit_label = "استارز" if ptype == PRODUCT_TYPE_TELEGRAM_STARS else "بوست"
        text = (
            f"🛒 <b>{p['name']}</b>\n\n"
            f"📝 <b>توضیحات:</b>\n{p['description']}\n\n"
            f"💵 قیمت هر واحد: <b>${p['price_usd']:.4f}</b>\n"
            f"🇮🇷 معادل هر واحد: <b>{base_price_irt:,} تومان</b>\n\n"
            f"📦 وضعیت: {stock_txt}\n\n"
            f"<i>📌 تعداد {unit_label} مورد نظر را وارد کنید تا قیمت کل محاسبه شود.</i>"
        )
        if p["stock"] == 0:
            kb = _kb([_btn("❌ موجود نیست", cb="noop")], [_btn("بازگشت", cb=f"cat_{p['cat_id']}")])
        else:
            kb = _kb(
                [_btn(f"🔢 وارد کردن تعداد {unit_label}", cb=f"qty_input_{product_id}")],
                [_btn("بازگشت", cb=f"cat_{p['cat_id']}")],
            )
    else:
        extra_note = {
            PRODUCT_TYPE_TELEGRAM_PREMIUM: "\n\n📱 <i>پس از ثبت سفارش، یوزرنیم تلگرام شما درخواست می‌شود.</i>",
            PRODUCT_TYPE_CHATGPT:          "\n\n🤖 <i>پس از ثبت سفارش، آدرس ایمیل شما درخواست می‌شود.</i>",
            PRODUCT_TYPE_VIRTUAL_NUMBER:   "\n\n📞 <i>پس از ثبت سفارش، کشور و سرویس مورد نیاز درخواست می‌شود.</i>",
        }.get(ptype, "")
        text = (
            f"🛒 <b>{p['name']}</b>\n\n"
            f"📝 <b>توضیحات:</b>\n{p['description']}\n\n"
            f"💵 قیمت پایه: <b>{p['price_usd']:.2f} دلار</b>\n"
            f"🇮🇷 معادل تومانی: <b>{total_price:,} تومان</b>\n\n"
            f"📦 وضعیت محصول: {stock_txt}{extra_note}"
        )
        if p["stock"] == 0:
            kb = _kb([_btn("❌ موجود نیست", cb="noop")], [_btn("بازگشت", cb=f"cat_{p['cat_id']}")])
        else:
            _buy_emoji_map = {
                PRODUCT_TYPE_TELEGRAM_PREMIUM: EMOJI_PREMIUM,
                PRODUCT_TYPE_TELEGRAM_STARS:   EMOJI_STARS,
                PRODUCT_TYPE_CHATGPT:          EMOJI_CHATGPT,
                PRODUCT_TYPE_GEMINI:           EMOJI_GEMINI,
                PRODUCT_TYPE_BOOST:            EMOJI_BOOST,
                PRODUCT_TYPE_FRAGMENT:         EMOJI_FRAGMENT,
            }
            buy_emoji = _buy_emoji_map.get(ptype)
            saved_code = ctx.user_data.get("saved_discount_code")
            disc_note = f"\n\n🎟 کد تخفیف ذخیره‌شده: <code>{saved_code}</code> ✅" if saved_code else ""
            if disc_note:
                text += disc_note
            kb = _kb(
                [_btn("ثبت سفارش", cb=f"buy_{product_id}", emoji_id=buy_emoji)],
                [_btn("🎟 کد تخفیف", cb=f"apply_discount_{product_id}")],
                [_btn("بازگشت", cb=f"cat_{p['cat_id']}", emoji_id="5346112927688574301", style="danger")],
            )

    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_qty_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    product_id = int(q.data.split("_")[-1])
    p = db_mod.get_product(product_id)
    if not p:
        await q.answer("محصول یافت نشد!", show_alert=True)
        return
    ptype = _detect_product_type(p["name"], p["description"])
    unit_label = "استارز" if ptype == PRODUCT_TYPE_TELEGRAM_STARS else "بوست"
    min_qty    = 50 if ptype == PRODUCT_TYPE_TELEGRAM_STARS else 1
    min_note   = f"\n⚠️ <b>حداقل خرید: {min_qty} {unit_label}</b>" if min_qty > 1 else ""
    ctx.user_data["state"] = f"awaiting_qty_{product_id}"
    usd_irt = await _fetch_usd_to_irt()
    unit_price_irt = int(p["price_usd"] * usd_irt)
    await q.edit_message_text(
        f"🔢 <b>تعداد {unit_label}</b>\n\n"
        f"💵 قیمت هر واحد: <b>${p['price_usd']:.4f}</b>\n"
        f"🇮🇷 معادل هر واحد: <b>{unit_price_irt:,} تومان</b>"
        f"{min_note}\n\n"
        f"تعداد {unit_label} مورد نظر را وارد کنید:\n"
        f"مثال: <code>{min_qty}</code>",
        reply_markup=_kb([_btn("انصراف", cb=f"product_{product_id}")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_qty_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("state", "")
    if not state.startswith("awaiting_qty_"):
        return
    product_id = int(state.split("_")[-1])
    raw = update.message.text.strip().replace(",", "").replace("،", "")
    fa_map = str.maketrans("۰۱۲۳۴۵۶۷۸۹", "0123456789")
    raw = raw.translate(fa_map)
    if not raw.isdigit() or int(raw) <= 0:
        await update.message.reply_text(
            "❌ لطفاً یک عدد صحیح مثبت وارد کنید.\nمثال: <code>100</code>",
            parse_mode=ParseMode.HTML)
        return
    quantity = int(raw)
    p = db_mod.get_product(product_id)
    if not p:
        await update.message.reply_text("❌ محصول یافت نشد.")
        ctx.user_data["state"] = None
        return
    ptype = _detect_product_type(p["name"], p["description"])
    unit_label = "استارز" if ptype == PRODUCT_TYPE_TELEGRAM_STARS else "بوست"

    MIN_STARS_QTY = 50
    if ptype == PRODUCT_TYPE_TELEGRAM_STARS and quantity < MIN_STARS_QTY:
        await update.message.reply_text(
            f"⚠️ <b>حداقل خرید استارز {MIN_STARS_QTY} عدد است.</b>\n\n"
            f"شما وارد کردید: <b>{quantity}</b>\n"
            f"لطفاً عددی بزرگ‌تر یا مساوی <b>{MIN_STARS_QTY}</b> وارد کنید:\n"
            f"مثال: <code>{MIN_STARS_QTY}</code>",
            parse_mode=ParseMode.HTML,
        )
        return 

    usd_irt = await _fetch_usd_to_irt()
    unit_price_irt = int(p["price_usd"] * usd_irt)
    base_total_irt = unit_price_irt * quantity
    _, fee, fee_desc = _calculate_price_with_fee(base_total_irt, ptype)
    total_irt = base_total_irt + fee
    fee_line = f"\n💸 {fee_desc}" if fee > 0 else ""
    ctx.user_data["pending_order"] = {
        "product_id": product_id, "product_type": ptype,
        "quantity": quantity, "unit_price_irt": unit_price_irt,
        "base_price_irt": base_total_irt, "fee_irt": fee,
        "total_irt": total_irt, "cat_id": p["cat_id"],
        "discount_code": None, "discount_irt": 0,
    }

    discount_applied_txt = ""
    saved_code = ctx.user_data.get("saved_discount_code")
    if saved_code:
        disc_amount, disc_err = db_mod.validate_discount(
            saved_code, update.effective_user.id, total_irt)
        if not disc_err and disc_amount > 0:
            ctx.user_data["pending_order"]["discount_code"] = saved_code
            ctx.user_data["pending_order"]["discount_irt"] = disc_amount
            ctx.user_data["pending_order"]["total_irt"] = max(0, total_irt - disc_amount)
            total_irt = ctx.user_data["pending_order"]["total_irt"]
            ctx.user_data.pop("saved_discount_code", None)
            discount_applied_txt = (
                f"\n🎟 کد تخفیف: <code>{saved_code}</code>"
                f"\n💸 تخفیف: <b>-{disc_amount:,} تومان</b>"
            )

    user_row = db_mod.get_user(update.effective_user.id)
    balance = user_row["balance"] if user_row else 0
    shortage = max(0, total_irt - balance)

    price_line = _qty_summary_price_line(ctx.user_data["pending_order"], quantity, unit_label)
    summary_text = (
        f"✅ <b>خلاصه سفارش</b>\n\n"
        f"📦 محصول: <b>{p['name']}</b>\n"
        f"🔢 تعداد: <b>{quantity:,} {unit_label}</b>\n"
        f"{price_line}"
        f"{discount_applied_txt}"
        f"💳 موجودی شما: <b>{balance:,} تومان</b>\n"
    )
    if shortage > 0:
        summary_text += f"📉 کمبود: <b>{shortage:,} تومان</b>\n\n❌ موجودی کافی نیست."
        kb = _kb(
            [_btn("💳 شارژ حساب", cb="menu_topup")],
            [_btn("بازگشت به محصول", cb=f"product_{product_id}")],
        )
    else:
        if not discount_applied_txt:
            summary_text += "\n🎟 <i>اگر کد تخفیف دارید، قبل از ثبت وارد کنید.</i>"
        kb = _kb(
            [_btn("ثبت سفارش", cb=f"buy_{product_id}"),
             _btn("🎟 کد تخفیف", cb=f"apply_discount_{product_id}")],
            [_btn("بازگشت به محصول", cb=f"product_{product_id}")],
        )
    await update.message.reply_text(summary_text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_apply_discount(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    product_id = int(q.data.split("_")[-1])
    p = db_mod.get_product(product_id)
    if not p:
        await q.answer("محصول یافت نشد!", show_alert=True)
        return
    ctx.user_data["state"] = f"awaiting_discount_{product_id}"
    usd_irt = await _fetch_usd_to_irt()
    ptype = _detect_product_type(p["name"], p["description"])
    base_irt = int(p["price_usd"] * usd_irt)
    _, fee, _ = _calculate_price_with_fee(base_irt, ptype)
    total_irt = base_irt + fee
    await q.edit_message_text(
        f"🎟 <b>کد تخفیف</b>\n\n"
        f"📦 محصول: <b>{p['name']}</b>\n"
        f"💰 قیمت کنونی: <b>{total_irt:,} تومان</b>\n\n"
        "کد تخفیف خود را وارد کنید:\n"
        "مثال: <code>SUMMER10</code>",
        reply_markup=_kb(
            [_btn("انصراف — بدون تخفیف", cb=f"buy_{product_id}")],
            [_btn("بازگشت به محصول", cb=f"product_{product_id}")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def handle_discount_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("state", "")
    if not state.startswith("awaiting_discount_"):
        return
    product_id = int(state.split("_")[-1])
    ctx.user_data["state"] = None
    code = update.message.text.strip().upper()
    user_id = update.effective_user.id

    p = db_mod.get_product(product_id)
    if not p:
        await update.message.reply_text("❌ محصول یافت نشد.")
        return

    pending = ctx.user_data.get("pending_order", {})
    if pending.get("product_id") == product_id and pending.get("total_irt"):
        total_irt = pending["total_irt"]
        has_pending = True
    else:
        usd_irt = await _fetch_usd_to_irt()
        ptype = _detect_product_type(p["name"], p["description"])
        base_irt = int(p["price_usd"] * usd_irt)
        _, fee, _ = _calculate_price_with_fee(base_irt, ptype)
        total_irt = base_irt + fee
        has_pending = False

    discount, err = db_mod.validate_discount(code, user_id, total_irt)
    if err:
        await update.message.reply_text(
            f"{err}\n\nدوباره امتحان کنید یا بدون تخفیف ثبت کنید.",
            reply_markup=_kb(
                [_btn("🎟 کد دیگری وارد کن", cb=f"apply_discount_{product_id}")],
                [_btn("✅ ثبت بدون تخفیف", cb=f"buy_{product_id}")],
                [_btn("بازگشت به محصول", cb=f"product_{product_id}")],
            ),
            parse_mode=ParseMode.HTML,
        )
        return

    new_total = max(0, total_irt - discount)
    if has_pending:
        ctx.user_data["pending_order"]["discount_code"] = code
        ctx.user_data["pending_order"]["discount_irt"] = discount
        ctx.user_data["pending_order"]["total_irt"] = new_total
    else:
        ctx.user_data["saved_discount_code"] = code

    await update.message.reply_text(
        f"✅ <b>کد تخفیف اعمال شد!</b>\n\n"
        f"📦 محصول: <b>{p['name']}</b>\n"
        f"🎟 کد: <code>{code}</code>\n"
        f"💸 تخفیف: <b>-{discount:,} تومان</b>\n"
        f"💰 قیمت نهایی: <b>{new_total:,} تومان</b>",
        reply_markup=_kb(
            [_btn("ثبت سفارش", cb=f"buy_{product_id}")],
            [_btn("بازگشت به محصول", cb=f"product_{product_id}")],
        ),
        parse_mode=ParseMode.HTML,
    )

async def cb_buy_product(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer("⏳ صبور باشید ..", show_alert=True)
    if await _check_banned(update):
        return
    if not db_mod.is_section_active("shop"):
        await _section_closed_msg(update, "فروش")
        return

    product_id = int(q.data.split("_")[1])
    user_id = q.from_user.id
    p = db_mod.get_product(product_id)
    row = db_mod.get_user(user_id)
    if not p or not row:
        return

    usd_irt = await _fetch_usd_to_irt()
    ptype = _detect_product_type(p["name"], p["description"])
    pending = ctx.user_data.get("pending_order", {})

    if pending.get("product_id") == product_id and pending.get("total_irt"):
        base_price_irt = pending["base_price_irt"]
        fee = pending.get("fee_irt", 0)
        total_price = pending["total_irt"]
        quantity = pending.get("quantity", 1)
        discount_code = pending.get("discount_code")
        discount_irt = pending.get("discount_irt", 0)
        _, _, fee_desc = _calculate_price_with_fee(base_price_irt, ptype)
    else:
        base_price_irt = int(p["price_usd"] * usd_irt)
        _, fee, fee_desc = _calculate_price_with_fee(base_price_irt, ptype)
        total_price = base_price_irt + fee
        quantity = 1
        discount_code = None
        discount_irt = 0
        saved_code = ctx.user_data.get("saved_discount_code")
        if saved_code:
            disc_amount, disc_err = db_mod.validate_discount(saved_code, user_id, total_price)
            if not disc_err and disc_amount > 0:
                discount_code = saved_code
                discount_irt = disc_amount
                total_price = max(0, total_price - discount_irt)
                ctx.user_data.pop("saved_discount_code", None)

    if p["stock"] == 0:
        await q.answer("❌ متأسفانه این محصول تمام شده است!", show_alert=True)
        return

    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST) and quantity == 1 and not pending.get("quantity"):
        await q.edit_message_text(
            "🔢 برای این محصول ابتدا باید تعداد را مشخص کنید.",
            reply_markup=_kb([_btn("🔢 وارد کردن تعداد", cb=f"qty_input_{product_id}")],
                              [_btn("بازگشت", cb=f"product_{product_id}")]),
        )
        return

    threshold = _get_kyc_threshold()
    if total_price >= threshold and not row["is_verified"]:
        pending_kyc = db_mod.get_pending_kyc_by_user(user_id)
        fee_line = f"\n💸 {fee_desc}" if fee > 0 else ""
        if pending_kyc:
            await q.edit_message_text(
                f"⏳ <b>احراز هویت در انتظار تأیید</b>\n\n"
                f"📦 محصول: <b>{p['name']}</b>\n"
                f"💰 مبلغ: <b>{_fmt_balance(total_price)}</b>{fee_line}\n\n"
                "⏳ درخواست احراز هویت شما در صف بررسی ادمین است.",
                reply_markup=_kb([_btn("بازگشت", cb=f"product_{product_id}")],
                                  [_btn("منوی اصلی", cb="back_main", emoji_id="5346112927688574301", style="danger")]),
                parse_mode=ParseMode.HTML)
        else:
            ctx.user_data["kyc_pending_purchase"] = {
                "product_id": product_id, "product_type": ptype,
                "base_price_irt": base_price_irt, "fee": fee,
                "fee_desc": fee_desc, "total_price": total_price, "cat_id": p["cat_id"],
                "quantity": quantity,
            }
            ctx.user_data["state"] = "awaiting_kyc_card_number"
            await q.edit_message_text(
                KYC_INTRO_TEXT,
                reply_markup=_kb([_btn("✅ متوجه شدم، ادامه می‌دهم", cb=f"kyc_proceed_{product_id}")]),
                parse_mode=ParseMode.HTML, disable_web_page_preview=True)
        return

    if row["balance"] < total_price and not _needs_extra_info_before_checkout(ptype):
        shortage = total_price - row["balance"]
        checkout_pending = {
            "product_id": product_id, "product_type": ptype,
            "quantity": quantity, "base_price_irt": base_price_irt,
            "fee_irt": fee, "total_irt": total_price, "cat_id": p["cat_id"],
            "discount_code": discount_code, "discount_irt": discount_irt,
            "extra_info": None,
        }
        ctx.user_data["pending_checkout"] = checkout_pending
        await q.edit_message_text(
            _build_checkout_payment_text(checkout_pending, None, row["balance"]),
            reply_markup=InlineKeyboardMarkup(_checkout_payment_rows(product_id)),
            parse_mode=ParseMode.HTML,
        )
        return

    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST):
        unit_label = "استارز" if ptype == PRODUCT_TYPE_TELEGRAM_STARS else "بوست"
        ctx.user_data["pending_order"] = {
            "product_id": product_id, "product_type": ptype,
            "quantity": quantity, "base_price_irt": base_price_irt, "fee_irt": fee,
            "total_irt": total_price, "cat_id": p["cat_id"],
            "discount_code": discount_code, "discount_irt": discount_irt,
        }
        ctx.user_data["state"] = "awaiting_order_extra_info"
        po = ctx.user_data["pending_order"]
        target_prompt = _build_username_entry_text(po, ptype)
        await q.edit_message_text(
            target_prompt,
            reply_markup=_order_extra_info_kb(product_id, ptype),
            parse_mode=ParseMode.HTML,
        )
        return

    if _needs_extra_info_before_checkout(ptype) and ptype not in (
        PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST,
    ):
        ctx.user_data["pending_order"] = {
            "product_id": product_id, "product_type": ptype,
            "base_price_irt": base_price_irt, "fee_irt": fee,
            "total_irt": total_price, "price_irt": base_price_irt, "cat_id": p["cat_id"],
            "discount_code": discount_code, "discount_irt": discount_irt,
        }
        ctx.user_data["state"] = "awaiting_order_extra_info"
        await q.edit_message_text(
            _build_username_entry_text(ctx.user_data["pending_order"], ptype),
            reply_markup=_order_extra_info_kb(product_id, ptype),
            parse_mode=ParseMode.HTML,
        )
    else:
        await _process_direct_purchase(update, ctx, product_id, base_price_irt, fee, total_price,
                                        ptype, None, quantity=quantity,
                                        discount_code=discount_code, discount_irt=discount_irt)


async def cb_kyc_proceed(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    product_id = int(q.data.split("_")[-1])
    ctx.user_data["state"] = "awaiting_kyc_card_number"
    await q.edit_message_text(
        "ابتدا <b>شماره کارت بانکی</b> خود را وارد کنید (۱۶ رقم):",
        reply_markup=_kb([_btn("انصراف", cb=f"product_{product_id}")]),
        parse_mode=ParseMode.HTML)


async def cb_order_for_myself(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    product_id = int(q.data.split("_")[-1])
    user = q.from_user
    pending = ctx.user_data.get("pending_order", {})
    if not pending or pending.get("product_id") != product_id:
        await q.answer("اطلاعات سفارش منقضی شده. دوباره از محصول شروع کنید.", show_alert=True)
        return
    ptype = pending.get("product_type", PRODUCT_TYPE_GENERIC)
    if ptype not in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_TELEGRAM_PREMIUM):
        await q.answer("این گزینه فقط برای استارز و پریمیوم است.", show_alert=True)
        return
    if not user.username:
        await q.answer(
            "یوزرنیم تلگرام در پروفایل شما نیست. لطفاً دستی وارد کنید.",
            show_alert=True,
        )
        return
    await q.answer()
    extra_info = f"@{user.username.lstrip('@')}"
    await _delete_username_warning_message(ctx)
    await _send_username_warning_confirm(
        update, ctx, extra_info, ptype, via_callback=True,
    )


async def _process_direct_purchase(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE,
    product_id: int, price_irt: int, fee_irt: int, total_irt: int,
    ptype: str, extra_info: str | None,
    quantity: int = 1, discount_code: str | None = None, discount_irt: int = 0,
) -> None:
    if update.callback_query:
        user = update.callback_query.from_user
        chat_id = update.callback_query.message.chat_id

        async def send_msg(text, reply_markup=None, parse_mode=ParseMode.HTML, **kwargs):
            return await ctx.bot.send_message(
                chat_id=chat_id, text=text,
                reply_markup=reply_markup, parse_mode=parse_mode, **kwargs,
            )
    else:
        user = update.effective_user
        send_msg = update.message.reply_text

    p = db_mod.get_product(product_id)
    if not p:
        await send_msg("❌ خطا: محصول یافت نشد.")
        return
    row = db_mod.get_user(user.id)
    if not row or row["balance"] < total_irt:
        await send_msg("❌ موجودی حساب شما کافی نیست.",
                        reply_markup=_kb([_btn("💳 شارژ حساب", cb="menu_topup")]))
        return

    db_mod.update_balance(user.id, -total_irt)
    delivery_note = f"[تعداد: {quantity}] [اطلاعات: {extra_info}]" if extra_info else (f"[تعداد: {quantity}]" if quantity > 1 else None)
    order_id = db_mod.create_order(user.id, product_id, total_irt, p["name"], quantity=quantity)
    db_mod.update_order_status(order_id, "pending_review", delivery_note)
    with db_mod.db() as conn:
        conn.execute("UPDATE users SET total_spent=total_spent+? WHERE user_id=?", (total_irt, user.id))
    db_mod.update_level(user.id)

    if discount_code and discount_irt > 0:
        db_mod.use_discount_code(discount_code, user.id, order_id, discount_irt)

    ctx.user_data.pop("pending_order", None)

    if _is_telegram_api_product(ptype):
        api_result = await _handle_telegram_api_order(
            ctx, order_id, user.id, p, ptype, quantity, extra_info,
        )
        qty_txt_api = ""
        if ptype == PRODUCT_TYPE_TELEGRAM_STARS and quantity >= 1:
            qty_txt_api = f"\n🔢 تعداد: <b>{quantity:,} استارز</b>"
        api_order_txt = ""
        if api_result.get("ok"):
            api_order_txt = f"\n🧾 شماره سفارش API: <code>{_he(api_result.get('order'))}</code>"
            await send_msg(
                f"✅ <b>سفارشت ثبت شد!</b>\n\n"
                f"📦 محصول: <b>{_he(p['name'])}</b>{qty_txt_api}\n"
                f"🔗 لینک سفارش: <code>{_he(api_result.get('link'))}</code>\n"
                f"💳 مجموع کسرشده: <b>{_fmt_balance(total_irt)}</b>\n"
                f"🔖 شماره سفارش ربات: <b>#{order_id}</b>{api_order_txt}",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
            )
        else:
            await send_msg(
                f"✅ <b>سفارش شما در ربات ثبت شد.</b>\n\n"
                f"📦 محصول: <b>{_he(p['name'])}</b>{qty_txt_api}\n"
                f"💳 مجموع کسرشده: <b>{_fmt_balance(total_irt)}</b>\n"
                f"🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
                "⚠️ ثبت خودکار داخل API با خطا مواجه شد و خروجی برای ادمین ارسال شد. پشتیبانی سفارش را بررسی می‌کند.",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
            )
        ctx.user_data["state"] = None
        ctx.user_data["pending_order"] = {}
        return

    cat = db_mod.get_category(p["cat_id"])
    cat_name = f"{cat['emoji']} {cat['name']}" if cat else "—"
    qty_line_adm = _qty_line_for_order(ptype, quantity)
    admin_text = (
        "🛍 <b>سفارش جدید</b>\n\n"
        f"👤 نام: <b>{user.full_name}</b>\n🆔 شناسه: <code>{user.id}</code>\n"
        f"📛 یوزرنیم: @{user.username if user.username else '—'}\n\n"
        f"📦 محصول: <b>{p['name']}</b>\n📂 دسته: {cat_name}\n"
        f"{qty_line_adm}"
        f"{_build_extra_line(ptype, extra_info)}🔖 سفارش: <b>#{order_id}</b>"
    )
    await _notify_admins(ctx, "notify_order", admin_text, _order_admin_kb(order_id))

    qty_txt = ""
    if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST) and quantity >= 1:
        qty_txt = f"\n🔢 تعداد: <b>{quantity:,} {_qty_unit_label(ptype)}</b>"
    await send_msg(
        f"✅ <b>سفارش شما ثبت شد!</b>\n\n"
        f"📦 محصول: <b>{p['name']}</b>{qty_txt}\n"
        f"💳 مجموع کسرشده: <b>{_fmt_balance(total_irt)}</b>\n"
        f"🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
        "⏳ پس از تأیید ادمین، سفارش پردازش می‌شود.",
        reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
    )
    ctx.user_data["state"] = None
    ctx.user_data["pending_order"] = {}


async def cb_confirm_username_yes(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    if ctx.user_data.get("state") != "awaiting_username_confirm":
        await q.answer()
        return
    extra_info = ctx.user_data.pop("username_confirm", None)
    pending = ctx.user_data.get("pending_order", {})
    if not extra_info or not pending:
        await q.answer("اطلاعات سفارش یافت نشد.", show_alert=True)
        return
    await q.answer("✅ در حال ثبت سفارش...")
    product_id = pending.get("product_id")
    p = db_mod.get_product(product_id) if product_id else None
    product_name = p["name"] if p else "محصول"
    await _delete_username_warning_message(ctx)
    await ctx.bot.send_message(
        chat_id=q.message.chat_id,
        text=(
            f"✅ شما خرید محصول <b>{_he(product_name)}</b> را "
            f"برای آیدی <code>{_he(extra_info)}</code> تأیید کردید."
        ),
        parse_mode=ParseMode.HTML,
    )
    await _complete_order_after_extra_info(update, ctx, extra_info, via_callback=True)


async def _return_to_username_entry(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE, product_id: int,
) -> None:
    q = update.callback_query
    pending = ctx.user_data.get("pending_order", {})
    if not pending or pending.get("product_id") != product_id:
        await q.answer("اطلاعات سفارش منقضی شده. دوباره از محصول شروع کنید.", show_alert=True)
        return
    ptype = pending.get("product_type", PRODUCT_TYPE_GENERIC)
    await q.answer()
    await _delete_username_warning_message(ctx)
    ctx.user_data.pop("username_confirm", None)
    ctx.user_data["state"] = "awaiting_order_extra_info"
    await q.message.reply_text(
        _build_username_entry_text(pending, ptype),
        reply_markup=_order_extra_info_kb(product_id, ptype),
        parse_mode=ParseMode.HTML,
    )


async def cb_confirm_username_no(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    product_id = int(update.callback_query.data.split("_")[-1])
    await _return_to_username_entry(update, ctx, product_id)


async def cb_edit_username(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    product_id = int(update.callback_query.data.split("_")[-1])
    await _return_to_username_entry(update, ctx, product_id)


async def _complete_order_after_extra_info(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE, extra_info: str,
    *, via_callback: bool = False,
) -> None:
    pending = ctx.user_data.get("pending_order", {})
    ptype = pending.get("product_type", PRODUCT_TYPE_GENERIC)
    fee_irt = pending.get("fee_irt", 0)
    base_price_irt = pending.get("base_price_irt", pending.get("price_irt", 0))
    total_irt = pending.get("total_irt", base_price_irt + fee_irt)
    product_id = pending.get("product_id")
    quantity = int(pending.get("quantity", 1))
    discount_code = pending.get("discount_code")
    discount_irt = int(pending.get("discount_irt", 0) or 0)
    user_id = update.effective_user.id if not via_callback else update.callback_query.from_user.id
    row = db_mod.get_user(user_id)

    ctx.user_data["state"] = None
    ctx.user_data.pop("username_confirm", None)

    if not row or row["balance"] < total_irt:
        balance = row["balance"] if row else 0
        checkout_pending = {**pending, "extra_info": extra_info}
        ctx.user_data["pending_checkout"] = checkout_pending
        ctx.user_data["pending_order"] = {}
        pay_text = _build_checkout_payment_text(checkout_pending, extra_info, balance)
        pay_kb = InlineKeyboardMarkup(_checkout_payment_rows(product_id))
        if via_callback:
            await ctx.bot.send_message(
                chat_id=update.callback_query.message.chat_id,
                text=pay_text, reply_markup=pay_kb, parse_mode=ParseMode.HTML,
            )
        else:
            await update.message.reply_text(
                pay_text, reply_markup=pay_kb, parse_mode=ParseMode.HTML,
            )
        return

    ctx.user_data["pending_order"] = {}
    await _process_direct_purchase(
        update, ctx, product_id, base_price_irt, fee_irt, total_irt, ptype, extra_info,
        quantity=quantity, discount_code=discount_code, discount_irt=discount_irt,
    )


async def handle_order_extra_info(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_order_extra_info":
        return
    text = update.message.text.strip()
    pending = ctx.user_data.get("pending_order", {})
    ptype = pending.get("product_type", PRODUCT_TYPE_GENERIC)
    product_id = pending.get("product_id")

    if ptype == PRODUCT_TYPE_TELEGRAM_PREMIUM:
        val = text.lstrip("@").strip()
        if not _is_valid_telegram_username(val):
            await update.message.reply_text(
                "❌ یوزرنیم معتبر نیست. دوباره وارد کنید:\nمثال: <code>@myusername</code>",
                parse_mode=ParseMode.HTML,
            )
            return
        extra_info = f"@{val}"
        await _send_username_warning_confirm(update, ctx, extra_info, ptype)
        return
    elif ptype == PRODUCT_TYPE_TELEGRAM_STARS:
        val = text.lstrip("@").strip()
        if not _is_valid_telegram_username(val):
            await update.message.reply_text(
                "❌ یوزرنیم معتبر نیست.\nمثال: <code>@myusername</code>",
                parse_mode=ParseMode.HTML,
            )
            return
        extra_info = f"@{val}"
        await _send_username_warning_confirm(update, ctx, extra_info, ptype)
        return
    elif ptype == PRODUCT_TYPE_CHATGPT:
        email = text.strip().lower()
        if "@" not in email or "." not in email:
            await update.message.reply_text("❌ آدرس ایمیل معتبر نیست.\nمثال: <code>example@gmail.com</code>",
                                             parse_mode=ParseMode.HTML)
            return
        extra_info = email
    elif ptype == PRODUCT_TYPE_VIRTUAL_NUMBER:
        if len(text) < 3:
            await update.message.reply_text("❌ لطفاً کشور و سرویس را مشخص کنید.", parse_mode=ParseMode.HTML)
            return
        extra_info = text
    elif ptype == PRODUCT_TYPE_BOOST:
        val = text.lstrip("@").strip()
        if not _is_valid_telegram_username(val):
            await update.message.reply_text(
                "❌ یوزرنیم کانال/گروه معتبر نیست.\nمثال: <code>@mychannel</code>",
                parse_mode=ParseMode.HTML,
            )
            return
        extra_info = f"@{val}"
        await _send_username_warning_confirm(update, ctx, extra_info, ptype)
        return
    else:
        extra_info = text

    fee_irt = pending.get("fee_irt", 0)
    base_price_irt = pending.get("base_price_irt", pending.get("price_irt", 0))
    total_irt = pending.get("total_irt", base_price_irt + fee_irt)
    quantity = int(pending.get("quantity", 1))
    discount_code = pending.get("discount_code")
    discount_irt = int(pending.get("discount_irt", 0) or 0)
    ctx.user_data["state"] = None
    ctx.user_data["pending_order"] = {}
    await update.message.reply_text("⏳ در حال پردازش...")
    await _process_direct_purchase(
        update, ctx, product_id, base_price_irt, fee_irt, total_irt, ptype, extra_info,
        quantity=quantity, discount_code=discount_code, discount_irt=discount_irt,
    )

def _order_product_type(order: Any, product: Any | None) -> str:
    if product:
        return _detect_product_type(
            _row_get(product, "name", ""),
            _row_get(product, "description", "") or "",
        )
    snap = _row_get(order, "product_name_snapshot", "") or ""
    return _detect_product_type(snap)


async def _deliver_order_to_user(
    ctx: ContextTypes.DEFAULT_TYPE, order_id: int, order: Any,
    delivery_content: str | None = None,
) -> None:
    p = db_mod.get_product(order["product_id"]) if order["product_id"] else None
    ptype = _order_product_type(order, p)
    p_name = p["name"] if p else (order["product_name_snapshot"] or "محصول")

    if _is_activation_product(ptype):
        db_mod.update_order_status(order_id, "delivered", "تکمیل شده")
        user_msg = ORDER_SUCCESS_USER_TEXT.format(order_id=order_id)
    else:
        content = (delivery_content or "").strip()
        db_mod.update_order_status(order_id, "delivered", content)
        user_msg = (
            f"🎉 <b>سفارش شما تأیید شد!</b>\n\n"
            f"📦 محصول: <b>{p_name}</b>\n"
            f"🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
            f"📬 <b>محتوای محصول شما:</b>\n<code>{content}</code>\n\n"
            "متشکریم از خرید شما! 🙏"
        )

    referrer_id = db_mod.claim_referrer_bonus_for_referee(order["user_id"])
    if referrer_id:
        db_mod.update_balance(referrer_id, config.REFERRAL_BONUS)
        try:
            await ctx.bot.send_message(
                referrer_id,
                f"🎉 <b>پاداش زیرمجموعه دریافت شد!</b>\n\n"
                f"💰 <b>{_fmt_balance(config.REFERRAL_BONUS)}</b> به موجودی شما اضافه شد.",
                parse_mode=ParseMode.HTML,
            )
        except Exception:
            pass

    try:
        await ctx.bot.send_message(
            order["user_id"], user_msg,
            parse_mode=ParseMode.HTML, reply_markup=MAIN_MENU_KB,
        )
    except Exception as e:
        log.warning("ارسال پیام به کاربر %s ناموفق: %s", order["user_id"], e)


async def cb_adm_confirm_order(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    data = q.data or ""
    admin_id = q.from_user.id

    if not _is_admin(admin_id):
        await q.answer("⛔ شما ادمین نیستید.", show_alert=True)
        return

    try:
        order_id = _order_id_from_callback(data, "confirm")
    except ValueError:
        log.warning("[سفارش] callback نامعتبر تأیید: %s", data)
        await q.answer("❌ دکمه نامعتبر است.", show_alert=True)
        return

    order = db_mod.get_order(order_id)
    if not order:
        await q.answer("❌ سفارش یافت نشد!", show_alert=True)
        return
    if order["status"] not in ("pending_review", "pending"):
        await q.answer("⚠️ این سفارش قبلاً پردازش شده.", show_alert=True)
        return

    _set_admin_delivery_ctx(ctx, admin_id, order_id)

    p = db_mod.get_product(order["product_id"]) if order["product_id"] else None
    ptype = _order_product_type(order, p)
    p_name = _he(p["name"] if p else (order["product_name_snapshot"] or "—"))
    extra_del = order["delivery"] or ""
    extra_note = f"\n📋 اطلاعات سفارش: <code>{_he(extra_del)}</code>\n" if extra_del else ""
    qty = int(order["quantity"] or 1)
    qty_line = _qty_line_for_order(ptype, qty)
    activation_note = ""
    if _is_activation_product(ptype):
        activation_note = (
            "\n<i>محصول فعال‌سازی: به کاربر فقط پیام «تکمیل شد» نمایش داده می‌شود.</i>\n"
        )

    msg_text = (
        f"✅ <b>تأیید سفارش #{order_id}</b>\n\n"
        f"📦 محصول: <b>{p_name}</b>\n"
        f"{qty_line}{extra_note}{activation_note}\n"
        "📬 <b>محتوای محصول</b> را همین الان در یک پیام متنی بفرستید:\n"
        "<i>(لینک، کد، توضیحات و ...)</i>"
    )
    reject_kb = InlineKeyboardMarkup([[
        InlineKeyboardButton("❌ لغو سفارش", callback_data=f"adm_reject_order_{order_id}"),
    ]])

    try:
        await _edit_callback_message(q, msg_text, reject_kb)
        await q.answer("✅ منتظر محتوای تحویل هستم.", show_alert=False)
    except Exception as e:
        log.exception("[سفارش] خطا در ویرایش پیام تأیید #%s: %s", order_id, e)
        await ctx.bot.send_message(
            admin_id, msg_text, reply_markup=reject_kb, parse_mode=ParseMode.HTML,
        )
        await q.answer("✅ دستورالعمل در پیام جدید ارسال شد.", show_alert=False)


async def handle_adm_deliver_order(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    admin_id = update.effective_user.id
    if not _is_admin(admin_id):
        return
    order_id = _get_admin_delivery_order_id(ctx, admin_id)
    if order_id is None:
        return
    if not update.message or not update.message.text:
        await update.message.reply_text("❌ لطفاً محتوای تحویل را به صورت متن ارسال کنید.")
        return
    delivery_content = update.message.text.strip()
    if not delivery_content:
        await update.message.reply_text("❌ متن تحویل خالی است.")
        return
    order = db_mod.get_order(order_id)
    if not order:
        _clear_admin_delivery_ctx(ctx, admin_id)
        await update.message.reply_text("❌ سفارش یافت نشد.")
        return
    if order["status"] not in ("pending_review", "pending"):
        _clear_admin_delivery_ctx(ctx, admin_id)
        await update.message.reply_text("⚠️ این سفارش قبلاً پردازش شده است.")
        return
    _clear_admin_delivery_ctx(ctx, admin_id)
    p = db_mod.get_product(order["product_id"]) if order["product_id"] else None
    ptype = _order_product_type(order, p)
    await _deliver_order_to_user(ctx, order_id, order, delivery_content)
    user_row = db_mod.get_user(order["user_id"])
    extra_del = order["delivery"] or ""
    deliver_note = (
        "✅ تکمیل شده (فعال‌سازی)" if _is_activation_product(ptype)
        else f"📬 محتوای تحویل:\n<code>{delivery_content}</code>"
    )
    await update.message.reply_text(
        f"✅ <b>سفارش تحویل داده شد</b>\n\n"
        f"👤 نام: <b>{user_row['full_name'] if user_row else '—'}</b>\n"
        f"🆔 شناسه: <code>{order['user_id']}</code>\n\n"
        f"📦 محصول: <b>{p['name'] if p else '—'}</b>\n"
        f"🔖 سفارش: <b>#{order_id}</b>\n"
        f"📋 اطلاعات: <code>{extra_del}</code>\n\n{deliver_note}",
        parse_mode=ParseMode.HTML, reply_markup=ADMIN_MENU_KB,
    )


async def cb_adm_reject_order(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    data = q.data or ""
    admin_id = q.from_user.id

    if not _is_admin(admin_id):
        await q.answer("⛔ شما ادمین نیستید.", show_alert=True)
        return

    try:
        order_id = _order_id_from_callback(data, "reject")
    except ValueError:
        await q.answer("❌ دکمه نامعتبر است.", show_alert=True)
        return

    order = db_mod.get_order(order_id)
    if not order:
        await q.answer("❌ سفارش یافت نشد!", show_alert=True)
        return
    if order["status"] not in ("pending_review", "pending"):
        await q.answer("⚠️ این سفارش قبلاً پردازش شده.", show_alert=True)
        return

    _clear_admin_delivery_ctx(ctx, admin_id)
    db_mod.update_balance(order["user_id"], order["amount_irt"])
    with db_mod.db() as conn:
        conn.execute(
            "UPDATE users SET total_spent=MAX(0,total_spent-?) WHERE user_id=?",
            (order["amount_irt"], order["user_id"]),
        )
    db_mod.update_level(order["user_id"])
    db_mod.update_order_status(order_id, "cancelled")

    done_text = f"❌ <b>سفارش #{order_id} رد شد.</b>\nموجودی به کاربر بازگشت."
    try:
        await _edit_callback_message(q, done_text, None)
    except Exception:
        pass

    await q.answer("❌ سفارش رد شد.", show_alert=False)
    try:
        await ctx.bot.send_message(
            order["user_id"],
            f"❌ <b>سفارش #{order_id} شما رد شد.</b>\n\n"
            f"💰 مبلغ <b>{_fmt_balance(order['amount_irt'])}</b> به موجودی شما بازگشت.",
            parse_mode=ParseMode.HTML,
            reply_markup=MAIN_MENU_KB,
        )
    except Exception as e:
        log.warning("اطلاع رد سفارش به کاربر %s ناموفق: %s", order["user_id"], e)

async def _process_checkout_order(
    ctx: ContextTypes.DEFAULT_TYPE, user_id: int, checkout: dict,
) -> dict[str, Any] | None:
    product_id = int(checkout.get("product_id", 0))
    p = db_mod.get_product(product_id)
    if not p:
        return None
    total_irt = int(checkout.get("total_irt", 0))
    row = db_mod.get_user(user_id)
    if not row or row["balance"] < total_irt:
        return None

    ptype = checkout.get("product_type") or _detect_product_type(p["name"], p["description"])
    quantity = int(checkout.get("quantity", 1))
    extra_info = checkout.get("extra_info")
    fee_irt = int(checkout.get("fee_irt", 0))
    discount_code = checkout.get("discount_code")
    discount_irt = int(checkout.get("discount_irt", 0) or 0)

    db_mod.update_balance(user_id, -total_irt)
    delivery_note = (
        f"[تعداد: {quantity}] [اطلاعات: {extra_info}]" if extra_info
        else (f"[تعداد: {quantity}]" if quantity > 1 else None)
    )
    order_id = db_mod.create_order(user_id, product_id, total_irt, p["name"], quantity=quantity)
    db_mod.update_order_status(order_id, "pending_review", delivery_note)
    with db_mod.db() as conn:
        conn.execute(
            "UPDATE users SET total_spent=total_spent+? WHERE user_id=?",
            (total_irt, user_id),
        )
    db_mod.update_level(user_id)
    if discount_code and discount_irt > 0:
        db_mod.use_discount_code(discount_code, user_id, order_id, discount_irt)

    if _is_telegram_api_product(ptype):
        api_result = await _handle_telegram_api_order(
            ctx, order_id, user_id, p, ptype, quantity, extra_info,
        )
        return {
            "order_id": order_id,
            "api": True,
            "api_ok": bool(api_result.get("ok")),
            "api_order": api_result.get("order"),
            "api_link": api_result.get("link"),
            "api_result": api_result,
        }

    urow = db_mod.get_user(user_id)
    full_name = urow["full_name"] if urow else str(user_id)
    username = urow["username"] if urow else None
    cat = db_mod.get_category(p["cat_id"])
    cat_name = f"{cat['emoji']} {cat['name']}" if cat else "—"
    admin_text = (
        "🛍 <b>سفارش جدید — پرداخت مستقیم</b>\n\n"
        f"👤 نام: <b>{full_name}</b>\n🆔 شناسه: <code>{user_id}</code>\n"
        f"📛 یوزرنیم: @{username if username else '—'}\n\n"
        f"📦 محصول: <b>{p['name']}</b>\n📂 دسته: {cat_name}\n"
        f"{_qty_line_for_order(ptype, quantity)}"
        f"{_build_extra_line(ptype, extra_info)}🔖 سفارش: <b>#{order_id}</b>"
    )
    await _notify_admins(ctx, "notify_order", admin_text, _order_admin_kb(order_id))
    return {"order_id": order_id, "api": False, "api_ok": None}


async def cb_checkout_pay(update: Update, ctx: ContextTypes.DEFAULT_TYPE, method: str) -> None:
    q = update.callback_query
    await q.answer()
    checkout = ctx.user_data.get("pending_checkout")
    if not checkout:
        await q.answer("اطلاعات سفارش یافت نشد. دوباره تلاش کنید.", show_alert=True)
        return
    row = db_mod.get_user(q.from_user.id)
    shortage = max(0, int(checkout["total_irt"]) - (row["balance"] if row else 0))
    ctx.user_data["checkout_linked"] = True

    if method == "card":
        if not db_mod.is_section_active("topup_card"):
            await _section_closed_msg(update, "پرداخت کارتی")
            return
        card = db_mod.get_setting("card_number")
        if not card:
            await q.edit_message_text("⚠️ شماره کارت تنظیم نشده.")
            return
        ctx.user_data["state"] = "awaiting_card_deposit_amount"
        ctx.user_data["pending_checkout_method"] = "card"
        await q.edit_message_text(
            f"💳 <b>پرداخت کارت به کارت</b>\n\n"
            f"📉 حداقل مبلغ این سفارش: <b>{_fmt_balance(shortage)}</b>\n\n"
            "مبلغ واریزی را به <b>تومان</b> وارد کنید:\nمثال: <code>200000</code>",
            reply_markup=_kb([_btn("انصراف", cb=f"product_{checkout['product_id']}")]),
            parse_mode=ParseMode.HTML,
        )
    elif method == "crypto":
        if not db_mod.is_section_active("topup_crypto"):
            await _section_closed_msg(update, "پرداخت ارزی")
            return
        await _show_crypto_coin_list(update, ctx, linked_checkout=True)
    elif method == "gateway":
        if not db_mod.is_section_active("topup_gateway"):
            await _section_closed_msg(update, "پرداخت درگاهی")
            return
        ctx.user_data["state"] = "awaiting_gateway_deposit_amount"
        ctx.user_data["pending_checkout_method"] = "gateway"
        await q.edit_message_text(
            f"🌐 <b>پرداخت درگاهی</b>\n\n"
            f"📉 حداقل مبلغ این سفارش: <b>{_fmt_balance(shortage)}</b>\n\n"
            "مبلغ پرداخت را به <b>تومان</b> وارد کنید:",
            reply_markup=_kb([_btn("انصراف", cb=f"product_{checkout['product_id']}")]),
            parse_mode=ParseMode.HTML,
        )


async def _show_crypto_coin_list(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE, *, linked_checkout: bool = False,
) -> None:
    q = update.callback_query
    rows = []
    for key, info in config.CRYPTO_DEPOSIT_OPTIONS.items():
        wallet = db_mod.get_setting(info["wallet_key"])
        if not wallet:
            continue
        label = f"{info['emoji']} {info['name']} — {info['network']}"
        rows.append([_btn(label, cb=f"crypto_pick_{key}")])
    if not rows:
        await q.edit_message_text(
            "⚠️ هیچ آدرس ولتی تنظیم نشده. با پشتیبانی تماس بگیرید.",
            reply_markup=_kb([_btn("بازگشت", cb="menu_topup", emoji_id="5346112927688574301")]),
        )
        return
    back_cb = "menu_topup" if not linked_checkout else f"product_{ctx.user_data.get('pending_checkout', {}).get('product_id', 0)}"
    rows.append([_btn("بازگشت", cb=back_cb, emoji_id="5346112927688574301")])
    title = "💰 <b>واریز با رمز ارز</b>\n\nارز مورد نظر را انتخاب کنید:"
    if linked_checkout:
        title = "💰 <b>پرداخت ارزی سفارش</b>\n\nارز مورد نظر را انتخاب کنید:"
    await q.edit_message_text(title, reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_crypto_pick(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    coin_key = q.data.replace("crypto_pick_", "", 1)
    info = config.CRYPTO_DEPOSIT_OPTIONS.get(coin_key)
    if not info:
        await q.answer("ارز نامعتبر", show_alert=True)
        return
    wallet = db_mod.get_setting(info["wallet_key"])
    if not wallet:
        await q.answer("آدرس ولت تنظیم نشده!", show_alert=True)
        return
    ctx.user_data["pending_crypto_coin"] = coin_key
    ctx.user_data["state"] = "awaiting_crypto_deposit_amount"
    min_irt = config.CRYPTO_MIN_DEPOSIT_IRT
    await q.edit_message_text(
        f"{info['emoji']} <b>{info['name']}</b>\n🌐 شبکه: <b>{info['network']}</b>\n\n"
        f"📬 آدرس ولت:\n<code>{wallet}</code>\n\n"
        "⚠️ <b>نکات مهم:</b>\n"
        f"• حتماً به شبکه <b>{info['network']}</b> واریز کنید؛ واریز با شبکه اشتباه باعث از دست رفتن دارایی می‌شود.\n"
        f"• مبالغ کمتر از <b>{min_irt:,} تومان</b> شارژ نمی‌شوند.\n\n"
        "پس از واریز، تصویر رسید را ارسال کنید.\n\n"
        "<i>برای ادامه، مبلغ تقریبی واریز را به تومان وارد کنید (اختیاری — می‌توانید مستقیم رسید بفرستید):</i>\n"
        "یا دکمه زیر را بزنید:",
        reply_markup=_kb(
            [_btn("📸 ارسال مستقیم رسید", cb="crypto_send_receipt")],
            [_btn("بازگشت", cb="topup_crypto", emoji_id="5346112927688574301")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def cb_crypto_send_receipt(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not ctx.user_data.get("pending_crypto_coin"):
        await q.answer("ابتدا ارز را انتخاب کنید.", show_alert=True)
        return
    ctx.user_data["state"] = "awaiting_deposit_photo"
    ctx.user_data["pending_deposit"] = {
        "method": f"crypto_{ctx.user_data['pending_crypto_coin']}",
        "declared_amount": 0,
    }
    await q.edit_message_text(
        "📸 لطفاً <b>تصویر رسید واریز</b> را ارسال کنید.",
        reply_markup=_kb([_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def cb_topup_menu(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if await _check_banned(update):
        return
    if not db_mod.is_section_active("topup"):
        await _section_closed_msg(update, "شارژ حساب")
        return
    usd_irt = await _fetch_usd_to_irt()
    row = db_mod.get_user(q.from_user.id)
    balance = row["balance"] if row else 0
    rows = []
    if db_mod.is_section_active("topup_crypto"):
        rows.append([_btn("💰 واریز با رمز ارز", cb="topup_crypto", emoji_id="5348392971207194994")])
    else:
        rows.append([_btn("🔒 واریز ارزی (غیرفعال)", cb="noop")])
    if db_mod.is_section_active("topup_card"):
        rows.append([_btn("پرداخت کارت به کارت", cb="topup_card", emoji_id="5346227465876423936")])
    else:
        rows.append([_btn("🔒 کارت به کارت (غیرفعال)", cb="noop")])
    if db_mod.is_section_active("topup_gateway"):
        rows.append([_btn("🌐 پرداخت درگاهی", cb="topup_gateway")])
    else:
        rows.append([_btn("🔒 درگاهی (غیرفعال)", cb="noop")])
    rows.append([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")])
    await q.edit_message_text(
        f"💳 <b>افزایش موجودی</b>\n\n"
        f"💰 موجودی فعلی شما: <b>{_fmt_balance(balance)}</b>\n"
        f"💱 نرخ دلار: <b>{usd_irt:,} تومان</b>\n\nروش پرداخت را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_topup_crypto(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("topup_crypto"):
        await _section_closed_msg(update, "واریز ارزی")
        return
    ctx.user_data.pop("checkout_linked", None)
    await _show_crypto_coin_list(update, ctx, linked_checkout=False)


async def cb_topup_gateway(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("topup_gateway"):
        await _section_closed_msg(update, "پرداخت درگاهی")
        return
    ctx.user_data["state"] = "awaiting_gateway_deposit_amount"
    ctx.user_data.pop("checkout_linked", None)
    await q.edit_message_text(
        "🌐 <b>پرداخت درگاهی</b>\n\n"
        "مبلغ پرداخت را به <b>تومان</b> وارد کنید:\nمثال: <code>200000</code>",
        reply_markup=_kb([_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def cb_topup_card(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("topup_card"):
        await _section_closed_msg(update, "پرداخت کارت به کارت")
        return
    card = db_mod.get_setting("card_number")
    if not card:
        await q.edit_message_text("⚠️ شماره کارت هنوز تنظیم نشده.",
                                   reply_markup=_kb([_btn("بازگشت", cb="menu_topup", emoji_id="5346112927688574301")]))
        return
    ctx.user_data["state"] = "awaiting_card_deposit_amount"
    await q.edit_message_text(
        "💳 <b>پرداخت کارت به کارت</b>\n\n"
        "📝 <b>لطفاً مبلغ واریز را به تومان وارد کنید:</b>\nمثال: <code>200000</code>",
        reply_markup=_kb([_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_card_deposit_amount(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_card_deposit_amount":
        return
    text = update.message.text.strip().replace(",", "").replace("،", "")
    if not text.isdigit():
        await update.message.reply_text("❌ مبلغ باید عدد صحیح به تومان باشد.\nمثال: <code>200000</code>",
                                         parse_mode=ParseMode.HTML)
        return
    amount = int(text)
    if amount <= 0:
        await update.message.reply_text("❌ مبلغ باید بیشتر از صفر باشد.")
        return

    user_id = update.effective_user.id
    row = db_mod.get_user(user_id)
    is_verified = row and row["is_verified"]

    if not is_verified:
        pending_kyc = db_mod.get_pending_kyc_by_user(user_id)
        if pending_kyc:
            ctx.user_data["state"] = None
            await update.message.reply_text(
                "⏳ <b>درخواست احراز هویت شما در انتظار بررسی است.</b>\n\n"
                "پس از تأیید ادمین می‌توانید واریز کارت به کارت انجام دهید.",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
        else:
            ctx.user_data["kyc_after_deposit"] = {"method": "card", "declared_amount": amount}
            ctx.user_data["state"] = "awaiting_kyc_card_number"
            await update.message.reply_text(
                KYC_INTRO_TEXT,
                reply_markup=_kb([_btn("✅ متوجه شدم، ادامه می‌دهم", cb="kyc_proceed_deposit")]),
                parse_mode=ParseMode.HTML, disable_web_page_preview=True)
        return

    card = db_mod.get_setting("card_number")
    holder = db_mod.get_setting("card_holder")
    card_fmt = "-".join([card[i:i+4] for i in range(0, 16, 4)]) if len(card) == 16 else card
    kyc_card = db_mod.get_approved_kyc_card(user_id)
    kyc_line = ""
    if kyc_card:
        kf = "-".join([kyc_card[i:i+4] for i in range(0, len(kyc_card), 4)]) if len(kyc_card) >= 16 else kyc_card
        kyc_line = f"\n\n✅ <b>فقط با کارت مجاز:</b> <code>{kf}</code>\n<i>پرداخت با کارت دیگری تأیید نخواهد شد.</i>"

    ctx.user_data["state"] = "awaiting_deposit_photo"
    pending_dep: dict = {"method": "card", "declared_amount": amount}
    if ctx.user_data.get("checkout_linked") and ctx.user_data.get("pending_checkout"):
        pending_dep["checkout"] = ctx.user_data["pending_checkout"]
    ctx.user_data["pending_deposit"] = pending_dep
    await update.message.reply_text(
        f"💳 <b>پرداخت کارت به کارت</b>\n\n"
        f"💵 مبلغ: <b>{amount:,} تومان</b>\n\n"
        f"🏦 شماره کارت:\n<b><code>{card_fmt}</code></b>\n"
        f"👤 به نام: <b>{holder}</b>{kyc_line}\n\nپس از واریز، تصویر رسید را ارسال کنید.",
        reply_markup=_kb([_copy_btn("📋 کپی شماره کارت", card.replace("-", "").replace(" ", ""))],
                          [_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML)


async def handle_crypto_deposit_amount(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_crypto_deposit_amount":
        return
    coin_key = ctx.user_data.get("pending_crypto_coin")
    info = config.CRYPTO_DEPOSIT_OPTIONS.get(coin_key or "")
    if not info:
        return
    text = update.message.text.strip().replace(",", "").replace("،", "")
    amount = 0
    if text.isdigit():
        amount = int(text)
        if amount > 0 and amount < config.CRYPTO_MIN_DEPOSIT_IRT:
            await update.message.reply_text(
                f"⚠️ حداقل واریز <b>{config.CRYPTO_MIN_DEPOSIT_IRT:,} تومان</b> است.",
                parse_mode=ParseMode.HTML,
            )
            return

    wallet = db_mod.get_setting(info["wallet_key"])
    ctx.user_data["state"] = "awaiting_deposit_photo"
    pending_dep: dict = {"method": f"crypto_{coin_key}", "declared_amount": amount}
    if ctx.user_data.get("checkout_linked") and ctx.user_data.get("pending_checkout"):
        pending_dep["checkout"] = ctx.user_data["pending_checkout"]
    ctx.user_data["pending_deposit"] = pending_dep

    equiv_line = ""
    if amount > 0:
        usd_irt = await _fetch_usd_to_irt()
        crypto_usd, _ = await _fetch_crypto_price_usd(info["price_key"])
        amount_crypto = round((amount / usd_irt) / crypto_usd, 6) if usd_irt and crypto_usd else 0
        equiv_line = f"\n💱 معادل تقریبی: <b>{amount_crypto} {info['symbol']}</b>\n"

    await update.message.reply_text(
        f"{info['emoji']} <b>{info['name']}</b> — شبکه <b>{info['network']}</b>\n"
        f"{equiv_line}\n📬 <b>آدرس ولت:</b>\n<code>{wallet}</code>\n\n"
        "⚠️ به شبکه واریز دقت کنید؛ واریز اشتباه = از دست رفتن دارایی.\n"
        f"⚠️ کمتر از <b>{config.CRYPTO_MIN_DEPOSIT_IRT:,} تومان</b> شارژ نمی‌شود.\n\n"
        "پس از واریز، <b>تصویر رسید</b> را ارسال کنید.",
        reply_markup=_kb(
            [_copy_btn(f"📋 کپی آدرس", wallet)],
            [_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def handle_gateway_deposit_amount(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_gateway_deposit_amount":
        return
    text = update.message.text.strip().replace(",", "").replace("،", "")
    if not text.isdigit() or int(text) <= 0:
        await update.message.reply_text("❌ مبلغ باید عدد صحیح مثبت باشد.", parse_mode=ParseMode.HTML)
        return
    amount = int(text)
    user_id = update.effective_user.id
    row = db_mod.get_user(user_id)
    if not (row and row["is_verified"]):
        pending_kyc = db_mod.get_pending_kyc_by_user(user_id)
        if pending_kyc:
            ctx.user_data["state"] = None
            await update.message.reply_text(
                "⏳ <b>احراز هویت در انتظار تأیید است.</b>",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
            )
        else:
            ctx.user_data["kyc_after_deposit"] = {"method": "gateway", "declared_amount": amount}
            ctx.user_data["state"] = "awaiting_kyc_card_number"
            await update.message.reply_text(
                KYC_INTRO_TEXT,
                reply_markup=_kb([_btn("✅ متوجه شدم", cb="kyc_proceed_deposit")]),
                parse_mode=ParseMode.HTML, disable_web_page_preview=True,
            )
        return

    title = db_mod.get_setting("gateway_title") or "پرداخت درگاهی"
    instructions = db_mod.get_setting("gateway_instructions") or ""
    ctx.user_data["state"] = "awaiting_deposit_photo"
    pending_dep: dict = {"method": "gateway", "declared_amount": amount}
    if ctx.user_data.get("checkout_linked") and ctx.user_data.get("pending_checkout"):
        pending_dep["checkout"] = ctx.user_data["pending_checkout"]
    ctx.user_data["pending_deposit"] = pending_dep
    await update.message.reply_text(
        f"🌐 <b>{title}</b>\n\n"
        f"💵 مبلغ: <b>{amount:,} تومان</b>\n\n"
        f"{instructions}\n\n"
        "پس از پرداخت، <b>تصویر رسید</b> را ارسال کنید.",
        reply_markup=_kb([_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_deposit_photo(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_deposit_photo":
        return
    if not update.message.photo:
        await update.message.reply_text("📸 لطفاً فقط <b>تصویر</b> رسید را ارسال کنید.", parse_mode=ParseMode.HTML)
        return

    deposit_info = ctx.user_data.get("pending_deposit", {})
    method = deposit_info.get("method", "unknown")
    user = update.effective_user
    row = db_mod.get_user(user.id)
    is_card = method == "card"
    is_gateway = method == "gateway"
    needs_kyc = is_card or is_gateway

    if needs_kyc and not (row and row["is_verified"]):
        pending_kyc = db_mod.get_pending_kyc_by_user(user.id)
        if pending_kyc:
            ctx.user_data["state"] = None
            await update.message.reply_text(
                "⏳ <b>درخواست احراز هویت شما در انتظار بررسی است.</b>\n\n"
                "پس از تأیید ادمین می‌توانید واریز کارت به کارت انجام دهید.",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
            return
        else:
            ctx.user_data["state"] = "awaiting_kyc_card_number"
            ctx.user_data["kyc_after_deposit"] = deposit_info
            await update.message.reply_text(
                KYC_INTRO_TEXT,
                reply_markup=_kb([_btn("✅ متوجه شدم، ادامه می‌دهم", cb="kyc_proceed_deposit")]),
                parse_mode=ParseMode.HTML, disable_web_page_preview=True)
            return

    tx_ref = "awaiting_confirmation"
    checkout = deposit_info.get("checkout")
    if checkout:
        tx_ref = db_mod.encode_checkout_ref(checkout)

    dep_id = db_mod.create_deposit(user.id, method, 0, tx_ref)
    ctx.user_data["state"] = None
    ctx.user_data["pending_deposit"] = {}
    ctx.user_data.pop("pending_checkout", None)
    ctx.user_data.pop("checkout_linked", None)
    ctx.user_data.pop("pending_crypto_coin", None)

    if method == "card":
        method_display = "کارت به کارت"
    elif method == "gateway":
        method_display = "درگاهی"
    elif method.startswith("crypto_"):
        coin_key = method.replace("crypto_", "", 1)
        cinfo = config.CRYPTO_DEPOSIT_OPTIONS.get(coin_key, {})
        method_display = f"ارزی {cinfo.get('name', coin_key)} ({cinfo.get('network', '')})"
    else:
        method_display = method
    kyc_status = "✅ احراز هویت شده" if (row and row["is_verified"]) else "❌ احراز نشده"
    kyc_card_info = ""
    if is_card and row and row["is_verified"]:
        kc = db_mod.get_approved_kyc_card(user.id)
        if kc:
            kf = "-".join([kc[i:i+4] for i in range(0, len(kc), 4)]) if len(kc) >= 16 else kc
            kyc_card_info = f"\n💳 کارت مجاز: <code>{kf}</code>"

    admin_text = (
        f"🔔 <b>درخواست شارژ حساب — {method_display}</b>\n\n"
        f"👤 نام: <b>{user.full_name}</b>\n🆔 شناسه: <code>{user.id}</code>\n"
        f"📛 یوزرنیم: @{user.username if user.username else '—'}\n"
        f"🔐 احراز هویت: {kyc_status}{kyc_card_info}\n\n"
        f"🆔 شناسه واریز: <b>#{dep_id}</b>\n\nمبلغ واریزی را تایید و وارد کنید:"
    )
    admin_kb = _kb([_btn(f"✅ تایید واریز #{dep_id}", cb=f"confirm_dep_{dep_id}"),
                    _btn(f"❌ رد واریز #{dep_id}", cb=f"reject_dep_{dep_id}")])
    photo_file_id = update.message.photo[-1].file_id
    checkout_note = ""
    if checkout:
        checkout_note = "\n🛒 <b>سفارش پس از تأیید واریز خودکار ثبت می‌شود.</b>\n"
    admin_text += checkout_note
    await _notify_admins(
        ctx, "notify_deposit", admin_text, admin_kb, photo_file_id=photo_file_id,
    )

    user_note = (
        "پس از بررسی، سفارش شما ثبت می‌شود."
        if checkout else "پس از بررسی ادمین، موجودی شما شارژ می‌شود."
    )
    await update.message.reply_text(
        f"✅ <b>رسید شما ارسال شد!</b>\n\n🆔 شناسه واریز: <b>#{dep_id}</b>\n\n{user_note}",
        reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
    )
    await _react(ctx.bot, update.message.chat_id, update.message.message_id, "👍")


async def cb_confirm_deposit(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    dep_id = int(q.data.split("_")[-1])
    dep = db_mod.get_deposit(dep_id)
    if not dep or dep["status"] != "pending":
        await q.edit_message_caption(caption=(q.message.caption or "") + "\n\n⚠️ این واریز قبلاً پردازش شده.",
                                      parse_mode=ParseMode.HTML)
        return
    ctx.user_data["adm_state"] = f"adm_dep_amount_{dep_id}"
    try:
        await q.edit_message_caption(
            caption=(q.message.caption or "") + "\n\n✅ <b>لطفاً مبلغ تایید شده را به تومان وارد کنید:</b>\nمثال: <code>500000</code>",
            reply_markup=_kb([_btn("❌ رد واریز", cb=f"reject_dep_{dep_id}")]),
            parse_mode=ParseMode.HTML)
    except Exception:
        await ctx.bot.send_message(q.from_user.id,
            f"✅ واریز #{dep_id}\nمبلغ را به تومان وارد کنید:\nمثال: <code>500000</code>",
            reply_markup=_kb([_btn("❌ رد واریز", cb=f"reject_dep_{dep_id}")]),
            parse_mode=ParseMode.HTML)


async def handle_adm_dep_amount(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_dep_amount_") or not _is_admin(update.effective_user.id):
        return
    dep_id = int(state.split("_")[-1])
    ctx.user_data["adm_state"] = None
    text = update.message.text.strip().replace(",", "")
    if not text.isdigit():
        await update.message.reply_text("❌ مبلغ باید عدد صحیح باشد.")
        ctx.user_data["adm_state"] = f"adm_dep_amount_{dep_id}"
        return
    amount = int(text)
    dep = db_mod.get_deposit(dep_id)
    if not dep or dep["status"] != "pending":
        await update.message.reply_text("⚠️ این واریز قبلاً پردازش شده.")
        return
    with db_mod.db() as conn:
        conn.execute("UPDATE deposits SET amount_irt=? WHERE deposit_id=?", (amount, dep_id))
    checkout = db_mod.decode_checkout_ref(dep["tx_ref"] or "")
    order_result = None
    order_id = None
    if checkout:
        db_mod.confirm_deposit(dep_id)
        order_result = await _process_checkout_order(ctx, dep["user_id"], checkout)
        order_id = order_result.get("order_id") if order_result else None
        user_row = db_mod.get_user(dep["user_id"])
        admin_order_line = "⚠️ ثبت سفارش ناموفق بود."
        if order_id and order_result.get("api") and order_result.get("api_ok"):
            admin_order_line = f"🛒 سفارش: <b>#{order_id}</b> ثبت شد و داخل API هم ثبت شد."
        elif order_id and order_result.get("api"):
            admin_order_line = f"🛒 سفارش: <b>#{order_id}</b> ثبت شد اما ثبت API خطا داد."
        elif order_id:
            admin_order_line = f"🛒 سفارش: <b>#{order_id}</b> ثبت شد."
        await update.message.reply_text(
            f"✅ <b>واریز #{dep_id} تأیید شد</b>\n\n"
            f"👤 کاربر: <b>{user_row['full_name'] if user_row else dep['user_id']}</b>\n"
            f"💰 مبلغ: <b>{_fmt_balance(amount)}</b>\n"
            f"{admin_order_line}",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML,
        )
        try:
            if order_id and order_result.get("api") and order_result.get("api_ok"):
                api_order_txt = f"\n🧾 شماره سفارش API: <code>{_he(order_result.get('api_order'))}</code>" if order_result.get("api_order") else ""
                await ctx.bot.send_message(
                    dep["user_id"],
                    f"🎉 <b>واریز شما تأیید شد و سفارشت ثبت شد!</b>\n\n"
                    f"💰 مبلغ: <b>{_fmt_balance(amount)}</b>\n"
                    f"🔖 شماره سفارش ربات: <b>#{order_id}</b>{api_order_txt}",
                    reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
                )
            elif order_id and order_result.get("api"):
                await ctx.bot.send_message(
                    dep["user_id"],
                    f"🎉 <b>واریز شما تأیید شد و سفارش شما در ربات ثبت شد.</b>\n\n"
                    f"💰 مبلغ: <b>{_fmt_balance(amount)}</b>\n"
                    f"🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
                    "⚠️ ثبت خودکار داخل API با خطا مواجه شد و خروجی برای ادمین ارسال شد. پشتیبانی سفارش را بررسی می‌کند.",
                    reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
                )
            elif order_id:
                await ctx.bot.send_message(
                    dep["user_id"],
                    f"🎉 <b>واریز و سفارش شما تأیید شد!</b>\n\n"
                    f"💰 مبلغ: <b>{_fmt_balance(amount)}</b>\n"
                    f"🔖 شماره سفارش: <b>#{order_id}</b>\n\n"
                    "⏳ سفارش در صف پردازش ادمین است.",
                    reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
                )
            else:
                await ctx.bot.send_message(
                    dep["user_id"],
                    f"🎉 <b>واریز تأیید شد!</b>\n\n💰 <b>{_fmt_balance(amount)}</b> به کیف پول اضافه شد.\n"
                    "لطفاً دوباره سفارش را ثبت کنید.",
                    reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
                )
        except Exception:
            pass
        return

    db_mod.confirm_deposit(dep_id)
    user_row = db_mod.get_user(dep["user_id"])
    await update.message.reply_text(
        f"✅ <b>واریز #{dep_id} تأیید شد</b>\n\n"
        f"👤 کاربر: <b>{user_row['full_name'] if user_row else dep['user_id']}</b>\n"
        f"💰 مبلغ: <b>{_fmt_balance(amount)}</b> به حساب اضافه شد.",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML,
    )
    try:
        await ctx.bot.send_message(
            dep["user_id"],
            f"🎉 <b>واریز شما تأیید شد!</b>\n\n💰 مبلغ <b>{_fmt_balance(amount)}</b> به کیف پول شما اضافه شد.",
            reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML,
        )
    except Exception:
        pass


async def cb_reject_deposit(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    dep_id = int(q.data.split("_")[-1])
    dep = db_mod.get_deposit(dep_id)
    if not dep:
        return
    with db_mod.db() as conn:
        conn.execute("UPDATE deposits SET status='rejected' WHERE deposit_id=?", (dep_id,))
    try:
        await q.edit_message_caption(
            caption=(q.message.caption or "") + f"\n\n❌ <b>واریز #{dep_id} رد شد.</b>",
            parse_mode=ParseMode.HTML)
    except Exception:
        await q.answer("واریز رد شد.", show_alert=True)
    try:
        await ctx.bot.send_message(dep["user_id"],
            "❌ متأسفانه واریز شما تأیید نشد.\nبرای پیگیری با پشتیبانی تماس بگیرید.",
            reply_markup=MAIN_MENU_KB)
    except Exception:
        pass

async def cb_referral(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("referral"):
        await _section_closed_msg(update, "زیرمجموعه‌گیری")
        return
    user_id = q.from_user.id
    bot_me = await ctx.bot.get_me()
    link = f"https://t.me/{bot_me.username}?start={user_id}"
    count = db_mod.get_referral_count(user_id)
    earned = count * config.REFERRAL_BONUS
    text = (
        "👥 <b>زیرمجموعه‌گیری</b>\n\n"
        f"🔗 لینک اختصاصی:\n<code>{link}</code>\n\n"
        f"👤 تعداد زیرمجموعه: <b>{count} نفر</b>\n"
        f"💰 درآمد کسب‌شده: <b>{_fmt_balance(earned)}</b>\n"
        f"🎁 پاداش هر نفر: <b>{_fmt_balance(config.REFERRAL_BONUS)}</b>"
    )
    kb = _kb([_copy_btn("📋 کپی لینک معرفی", link)], [_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")])
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)

async def cb_orders(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    orders = db_mod.get_user_orders(q.from_user.id)
    if not orders:
        await q.edit_message_text("📋 تاریخچه سفارشات شما خالی است.",
                                   reply_markup=_kb([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")]))
        return
    STATUS = {"pending": "⏳ در انتظار", "pending_review": "🔍 در حال بررسی",
               "delivered": "✅ تحویل داده شده", "cancelled": "❌ لغو شده"}
    lines = ["📋 <b>تاریخچه سفارشات</b>\n"]
    for o in orders[:15]:
        p = db_mod.get_product(o["product_id"]) if o["product_id"] else None
        p_name = (p["name"] if p else
                  (o["product_name_snapshot"] if o["product_name_snapshot"] else "محصول حذف‌شده"))
        status = STATUS.get(o["status"], o["status"])
        lines.append(f"🔖 <b>#{o['order_id']}</b> — {p_name}\n"
                      f"   💸 {_fmt_balance(o['amount_irt'])} | {status}\n"
                      f"   📅 {o['created_at'][:16]}\n")
    await q.edit_message_text("\n".join(lines),
                               reply_markup=_kb([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")]),
                               parse_mode=ParseMode.HTML)


async def cb_track_menu(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    ctx.user_data["state"] = "awaiting_order_id"
    await q.edit_message_text("🔍 <b>پیگیری سفارش</b>\n\nشماره سفارش خود را وارد کنید:",
                               reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631", style="danger")]),
                               parse_mode=ParseMode.HTML)


async def handle_order_track(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_order_id":
        return
    text = update.message.text.strip().lstrip("#")
    if not text.isdigit():
        await update.message.reply_text("❌ شماره سفارش نادرست است.")
        return
    ctx.user_data["state"] = None
    order = db_mod.get_order(int(text))
    if not order or order["user_id"] != update.effective_user.id:
        await update.message.reply_text("❌ سفارشی با این شماره یافت نشد.", reply_markup=MAIN_MENU_KB)
        return
    STATUS = {"pending": "⏳ در انتظار", "pending_review": "🔍 در حال بررسی ادمین",
               "delivered": "✅ تحویل داده شده", "cancelled": "❌ لغو شده"}
    p = db_mod.get_product(order["product_id"])
    delivery_txt = (f"\n\n📬 <b>محتوای محصول:</b>\n<code>{order['delivery']}</code>"
                    if order["delivery"] and not order["delivery"].startswith("[اطلاعات:") else "")
    await update.message.reply_text(
        f"🔍 <b>جزئیات سفارش #{order['order_id']}</b>\n\n"
        f"📦 محصول: <b>{p['name'] if p else 'نامشخص'}</b>\n"
        f"💸 مبلغ: <b>{_fmt_balance(order['amount_irt'])}</b>\n"
        f"📊 وضعیت: {STATUS.get(order['status'], order['status'])}\n"
        f"📅 تاریخ: {order['created_at'][:16]}{delivery_txt}",
        reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)

async def cb_support(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not db_mod.is_section_active("support"):
        await _section_closed_msg(update, "پشتیبانی")
        return
    ctx.user_data["state"] = "awaiting_support_msg"
    await q.edit_message_text("🎧 <b>پشتیبانی</b>\n\nپیام خود را بنویسید تا در اسرع وقت پاسخ دهیم:",
                               reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631", style="danger")]),
                               parse_mode=ParseMode.HTML)


async def handle_support_msg(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_support_msg":
        return
    ctx.user_data["state"] = None
    user = update.effective_user
    msg = update.message.text or ""
    await _notify_admins(
        ctx, "notify_support",
        f"🎧 <b>پیام پشتیبانی</b>\n\n👤 <b>{user.full_name}</b>\n"
        f"🆔 شناسه: <code>{user.id}</code>\n"
        f"📛 یوزرنیم: @{user.username if user.username else '—'}\n\n📝 پیام:\n{msg}",
        _kb([_btn(f"↩️ پاسخ به {user.id}", cb=f"reply_user_{user.id}")]),
    )
    await update.message.reply_text("✅ پیام شما ارسال شد. در اسرع وقت پاسخ خواهیم داد. 🙏",
                                     reply_markup=MAIN_MENU_KB)
    await _react(ctx.bot, update.message.chat_id, update.message.message_id, "💙")

async def cb_kyc_request(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    user_id = q.from_user.id
    row = db_mod.get_user(user_id)
    if row and row["is_verified"]:
        kyc_card = db_mod.get_approved_kyc_card(user_id)
        kf = "-".join([kyc_card[i:i+4] for i in range(0, len(kyc_card), 4)]) if kyc_card and len(kyc_card) >= 16 else (kyc_card or "—")
        await q.edit_message_text(
            f"✅ <b>شما قبلاً احراز هویت شده‌اید</b>\n\n💳 کارت مجاز: <code>{kf}</code>",
            reply_markup=_kb([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")]), parse_mode=ParseMode.HTML)
        return
    pending = db_mod.get_pending_kyc_by_user(user_id)
    if pending:
        await q.edit_message_text("⏳ <b>درخواست احراز هویت شما در صف بررسی است.</b>",
                                   reply_markup=_kb([_btn("بازگشت", cb="back_main", emoji_id="5346112927688574301", style="danger")]),
                                   parse_mode=ParseMode.HTML)
        return
    await q.edit_message_text(
        KYC_INTRO_TEXT,
        reply_markup=_kb([_btn("✅ متوجه شدم، ادامه می‌دهم", cb="kyc_proceed_voluntary")]),
        parse_mode=ParseMode.HTML, disable_web_page_preview=True)


async def cb_kyc_proceed_voluntary(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    ctx.user_data["state"] = "awaiting_kyc_card_number"
    await q.edit_message_text(
        "ابتدا <b>شماره کارت بانکی</b> خود را وارد کنید (۱۶ رقم):",
        reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631", style="danger")]), parse_mode=ParseMode.HTML)


async def cb_kyc_proceed_deposit(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    ctx.user_data["state"] = "awaiting_kyc_card_number"
    await q.edit_message_text(
        "ابتدا <b>شماره کارت بانکی</b> خود را وارد کنید (۱۶ رقم):",
        reply_markup=_kb([_btn("انصراف", cb="menu_topup", emoji_id="6030757850274336631", style="danger")]), parse_mode=ParseMode.HTML)


async def handle_kyc_card_number(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_kyc_card_number":
        return
    text = update.message.text.strip().replace("-", "").replace(" ", "")
    if not text.isdigit() or len(text) != 16:
        await update.message.reply_text("❌ شماره کارت باید ۱۶ رقم باشد. دوباره وارد کنید:",
                                         reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631")]))
        return
    ctx.user_data["kyc_card_number"] = text
    ctx.user_data["state"] = "awaiting_kyc_photo"
    card_fmt = "-".join([text[i:i+4] for i in range(0, 16, 4)])
    await update.message.reply_text(
        f"✅ شماره کارت <code>{card_fmt}</code> ثبت شد.\n\n" + KYC_INSTRUCTION_TEXT,
        reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_kyc_photo(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_kyc_photo":
        return
    if not update.message.photo:
        await update.message.reply_text("📸 لطفاً فقط <b>تصویر</b> ارسال کنید.", parse_mode=ParseMode.HTML)
        return
    user = update.effective_user
    card_number = ctx.user_data.pop("kyc_card_number", "")
    ctx.user_data.pop("kyc_after_deposit", None)
    ctx.user_data["state"] = None
    photo_file_id = update.message.photo[-1].file_id
    kyc_id = db_mod.create_kyc_request(user.id, photo_file_id, card_number)
    card_fmt = "-".join([card_number[i:i+4] for i in range(0, len(card_number), 4)]) if len(card_number) == 16 else card_number

    admin_text = (
        f"🔐 <b>درخواست احراز هویت جدید</b>\n\n"
        f"👤 نام: <b>{user.full_name}</b>\n🆔 شناسه: <code>{user.id}</code>\n"
        f"📛 یوزرنیم: @{user.username if user.username else '—'}\n\n"
        f"💳 شماره کارت: <code>{card_fmt}</code>\n🆔 شماره KYC: <b>#{kyc_id}</b>"
    )
    admin_kb = _kb([_btn(f"✅ تأیید KYC #{kyc_id}", cb=f"approve_kyc_{kyc_id}"),
                    _btn(f"❌ رد KYC #{kyc_id}", cb=f"reject_kyc_{kyc_id}")])
    await _notify_admins(
        ctx, "notify_kyc", admin_text, admin_kb, photo_file_id=photo_file_id,
    )

    await update.message.reply_text(
        f"✅ <b>درخواست احراز هویت ثبت شد!</b>\n\n"
        f"💳 کارت: <code>{card_fmt}</code>\n🆔 شماره درخواست: <b>#{kyc_id}</b>\n\n"
        "پس از بررسی ادمین، نتیجه به شما اعلام می‌شود.",
        reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
    await _react(ctx.bot, update.message.chat_id, update.message.message_id, "🔐")

def _is_admin(user_id: int) -> bool:
    if user_id in config.ADMIN_IDS:
        return True
    try:
        return user_id in db_mod.get_all_admins()
    except Exception:
        return False


ADMIN_MENU_KB = _kb(
    [_btn("📊 آمار کلی", cb="adm_stats"),          _btn("👥 مدیریت مشتریان", cb="adm_users")],
    [_btn("📢 ارسال همگانی", cb="adm_broadcast"),  _btn("↪️ فوروارد همگانی", cb="adm_forward")],
    [_btn("📂 افزودن دسته‌بندی", cb="adm_add_cat"), _btn("➕ افزودن محصول", cb="adm_add_product")],
    [_btn("🗂 مدیریت محصولات", cb="adm_manage_products"), _btn("🔒 کانال اجباری", cb="adm_forced_channel")],
    [_btn("📋 سفارشات جاری", cb="adm_pending_orders"), _btn("🪪 بررسی KYC", cb="adm_kyc_list")],
    [_btn("💼 تنظیم ولت", cb="adm_wallets"),       _btn("💳 تنظیم کارت", cb="adm_card")],
    [_btn("🌐 تنظیم درگاه", cb="adm_gateway"),    _btn("💰 تنظیم کارمزد", cb="adm_fee_settings")],
    [_btn("🎟 کدهای تخفیف", cb="adm_discounts"),   _btn("👑 مدیریت ادمین‌ها", cb="adm_admins")],
    [_btn("🔑 آستانه KYC", cb="adm_kyc_threshold"), _btn("⚙️ کنترل بخش‌ها", cb="adm_sections")],
    [_btn("📖 راهنمای ادمین", cb="adm_guide")],
)

NOTIF_LABELS = {
    "notify_registration": "👤 ثبت‌نام جدید",
    "notify_kyc": "🪪 احراز هویت جدید",
    "notify_deposit": "💰 شارژ موجودی",
    "notify_support": "💬 پیام پشتیبانی",
    "notify_order": "🛍 سفارش جدید",
}


async def cmd_admin(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if not _is_admin(update.effective_user.id):
        return
    await update.message.reply_text(
        f"👑 <b>پنل مدیریت</b>\n\n🕐 {_jalali_now()}",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)


async def cb_adm_pending_orders(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    orders = db_mod.get_pending_orders()
    if not orders:
        await q.edit_message_text(
            "📋 <b>سفارشات جاری</b>\n\n✅ سفارشی در انتظار تأیید نیست.",
            reply_markup=_kb([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")]),
            parse_mode=ParseMode.HTML,
        )
        return
    rows = []
    for o in orders[:20]:
        p = db_mod.get_product(o["product_id"]) if o["product_id"] else None
        p_name = p["name"] if p else (o["product_name_snapshot"] or "—")
        ptype = _order_product_type(o, p)
        qty = int(o["quantity"] or 1)
        qty_s = ""
        if ptype in (PRODUCT_TYPE_TELEGRAM_STARS, PRODUCT_TYPE_BOOST):
            qty_s = f" — {qty:,} {_qty_unit_label(ptype)}"
        uname = f"@{o['username']}" if o["username"] else str(o["user_id"])
        label = f"#{o['order_id']} {p_name}{qty_s} ({uname})"
        rows.append([_btn(label[:60], cb=f"adm_pending_view_{o['order_id']}")])
    rows.append([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        f"📋 <b>سفارشات جاری</b> ({len(orders)} مورد)\n\nبرای تأیید یا رد، سفارش را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML,
    )


async def cb_adm_pending_view(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    order_id = int(q.data.split("_")[-1])
    order = db_mod.get_order(order_id)
    if not order:
        await q.answer("سفارش یافت نشد!", show_alert=True)
        return
    p = db_mod.get_product(order["product_id"]) if order["product_id"] else None
    ptype = _order_product_type(order, p)
    u = db_mod.get_user(order["user_id"])
    uname = f"@{u['username']}" if u and u["username"] else "—"
    text = (
        f"📋 <b>سفارش #{order_id}</b>\n\n"
        f"👤 {_he(u['full_name'] if u else '—')} ({_he(uname)})\n"
        f"🆔 <code>{order['user_id']}</code>\n\n"
        f"📦 <b>{_he(p['name'] if p else order['product_name_snapshot'])}</b>\n"
        f"{_qty_line_for_order(ptype, int(order['quantity'] or 1))}"
        f"📋 <code>{_he(order['delivery'] or '—')}</code>\n"
        f"📅 {order['created_at'][:16]}"
    )
    back_kb = InlineKeyboardMarkup([[
        InlineKeyboardButton("بازگشت", callback_data="adm_pending_orders"),
    ]])
    combined_rows = list(_order_admin_kb(order_id).inline_keyboard) + list(back_kb.inline_keyboard)
    await q.edit_message_text(
        text,
        reply_markup=InlineKeyboardMarkup(combined_rows),
        parse_mode=ParseMode.HTML,
    )


async def cb_adm_notif_settings(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if q.from_user.id not in config.ADMIN_IDS:
        await q.answer("⛔ فقط ادمین ارشد.", show_alert=True)
        return
    target_id = int(q.data.split("_")[-1])
    prefs = db_mod.get_admin_notification_prefs(target_id)
    u = db_mod.get_user(target_id)
    name = u["full_name"] if u else str(target_id)
    rows = []
    for key, label in NOTIF_LABELS.items():
        on = prefs.get(key, True)
        icon = "✅" if on else "🔴"
        rows.append([_btn(f"{icon} {label}", cb=f"adm_toggle_notif_{target_id}_{key}")])
    rows.append([_btn("بازگشت", cb="adm_admins", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        f"🔔 <b>اعلان‌های ادمین</b>\n\n👤 <b>{name}</b> — <code>{target_id}</code>\n\n"
        "روی هر مورد کلیک کنید تا فعال/غیرفعال شود:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML,
    )


async def cb_adm_toggle_notif(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    if q.from_user.id not in config.ADMIN_IDS:
        await q.answer("⛔ فقط ادمین ارشد.", show_alert=True)
        return
    rest = q.data[len("adm_toggle_notif_"):]
    notif_key = None
    target_id = None
    for nk in NOTIF_LABELS:
        suffix = f"_{nk}"
        if rest.endswith(suffix):
            notif_key = nk
            target_id = int(rest[: -len(suffix)])
            break
    if notif_key is None or target_id is None:
        await q.answer("خطا در پردازش.", show_alert=True)
        return
    prefs = db_mod.get_admin_notification_prefs(target_id)
    new_val = not prefs.get(notif_key, True)
    db_mod.set_admin_notification(target_id, notif_key, new_val)
    await q.answer("✅ فعال شد" if new_val else "🔴 غیرفعال شد", show_alert=False)
    q.data = f"adm_notif_{target_id}"
    await cb_adm_notif_settings(update, ctx)


async def cb_adm_stats(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    stats = db_mod.get_stats()
    text = (
        f"📊 <b>آمار کلی ربات</b>\n\n"
        f"👤 کل کاربران: <b>{stats['users']:,}</b>\n"
        f"🚫 بن‌شده‌ها: <b>{stats['banned']:,}</b>\n"
        f"🪪 احراز هویت‌شده: <b>{stats.get('verified',0):,}</b>\n"
        f"⏳ KYC در انتظار: <b>{stats.get('pending_kyc',0):,}</b>\n"
        f"📦 محصولات فعال: <b>{stats['products']:,}</b>\n"
        f"📂 دسته‌بندی‌ها: <b>{stats['categories']:,}</b>\n"
        f"🛒 کل سفارشات: <b>{stats['orders']:,}</b>\n"
        f"✅ تحویل‌داده‌شده: <b>{stats['paid']:,}</b>\n"
        f"💰 درآمد کل: <b>{_fmt_balance(stats['revenue'])}</b>\n\n"
        f"⚙️ آستانه KYC: <b>{_fmt_balance(_get_kyc_threshold())}</b>\n"
        f"💸 کارمزد پریمیوم: <b>{_fmt_balance(_get_premium_fee())}</b>\n"
        f"💸 کارمزد استارز: <b>{_get_stars_fee_pct():.1f}%</b>\n\n"
        f"🕐 <i>{_jalali_now()}</i>"
    )
    await q.edit_message_text(text, reply_markup=_kb([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")]),
                               parse_mode=ParseMode.HTML)

async def cb_adm_users(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    total = db_mod.get_users_count()
    stats = db_mod.get_stats()
    await q.edit_message_text(
        f"👥 <b>مدیریت مشتریان</b>\n\n"
        f"👤 کل کاربران: <b>{total:,}</b>\n"
        f"🚫 بن‌شده: <b>{stats['banned']:,}</b>\n"
        f"🪪 احراز شده: <b>{stats.get('verified',0):,}</b>",
        reply_markup=_kb(
            [_btn("🔍 جستجوی کاربر", cb="adm_search_user")],
            [_btn("📋 لیست کاربران", cb="adm_user_list_0")],
            [_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")],
        ), parse_mode=ParseMode.HTML)


async def cb_adm_search_user(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_search_user"
    await q.edit_message_text(
        "🔍 <b>جستجوی کاربر</b>\n\n"
        "آیدی عددی، یوزرنیم یا نام کاربر را وارد کنید:\n"
        "مثال: <code>123456789</code> یا <code>@username</code>",
        reply_markup=_kb([_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")]), parse_mode=ParseMode.HTML)


async def handle_adm_search_user_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "adm_search_user" or not _is_admin(update.effective_user.id):
        return
    ctx.user_data["adm_state"] = None
    query = update.message.text.strip()
    results = db_mod.search_user(query)
    if not results:
        await update.message.reply_text(
            "❌ کاربری با این مشخصات یافت نشد.",
            reply_markup=_kb([_btn("🔍 جستجوی مجدد", cb="adm_search_user"),
                               _btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")]))
        return
    rows = []
    for u in results:
        uname = f"@{u['username']}" if u["username"] else f"#{u['user_id']}"
        icons = ("🚫 " if u["is_banned"] else "") + ("✅" if u["is_verified"] else "")
        rows.append([_btn(f"{icons} {u['full_name']} ({uname})", cb=f"adm_user_detail_{u['user_id']}")])
    rows.append([_btn("🔍 جستجوی مجدد", cb="adm_search_user")])
    rows.append([_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")])
    await update.message.reply_text(
        f"🔍 <b>نتایج جستجو</b> — {len(results)} کاربر:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_user_list(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    page = int(q.data.split("_")[-1])
    offset = page * 10
    users = db_mod.get_all_users(limit=10, offset=offset)
    total = db_mod.get_users_count()
    if not users:
        await q.edit_message_text("❌ کاربری یافت نشد.", reply_markup=_kb([_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")]))
        return
    rows = []
    for u in users:
        uname = f"@{u['username']}" if u["username"] else f"#{u['user_id']}"
        icons = ("🚫 " if u["is_banned"] else "") + ("✅" if u["is_verified"] else "")
        rows.append([_btn(f"{icons} {u['full_name']} ({uname})", cb=f"adm_user_detail_{u['user_id']}")])
    nav = []
    if page > 0:
        nav.append(_btn("◀️ قبلی", cb=f"adm_user_list_{page-1}"))
    if offset + 10 < total:
        nav.append(_btn("بعدی ▶️", cb=f"adm_user_list_{page+1}"))
    if nav:
        rows.append(nav)
    rows.append([_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        f"👥 <b>لیست کاربران</b> (صفحه {page+1})\n"
        f"نمایش {offset+1}–{min(offset+10, total)} از {total}",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_user_detail(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    u = db_mod.get_user(target_id)
    if not u:
        await q.answer("کاربر یافت نشد!", show_alert=True)
        return
    uname = f"@{u['username']}" if u["username"] else "ندارد"
    ban_status = "🚫 بن‌شده" if u["is_banned"] else "✅ فعال"
    ver_status = "✅ احراز شده" if u["is_verified"] else "❌ احراز نشده"
    kyc_card = db_mod.get_approved_kyc_card(target_id)
    kf = "-".join([kyc_card[i:i+4] for i in range(0, len(kyc_card), 4)]) if kyc_card and len(kyc_card) >= 16 else (kyc_card or "—")
    ref_count = db_mod.get_referral_count(target_id)
    orders = db_mod.get_user_orders(target_id)
    delivered = sum(1 for o in orders if o["status"] == "delivered")
    lvl_info = config.LEVEL_THRESHOLDS.get(u["level"], config.LEVEL_THRESHOLDS[0])
    text = (
        f"👤 <b>اطلاعات کاربر</b>\n\n"
        f"🆔 شناسه: <code>{target_id}</code>\n👤 نام: <b>{u['full_name']}</b>\n"
        f"📛 یوزرنیم: {uname}\n📅 تاریخ عضویت: {u['joined_at'][:10]}\n\n"
        f"💰 موجودی: <b>{_fmt_balance(u['balance'])}</b>\n"
        f"📈 کل خرید: <b>{_fmt_balance(u['total_spent'])}</b>\n"
        f"🛒 سفارشات تحویلی: <b>{delivered}</b>\n👥 زیرمجموعه: <b>{ref_count}</b>\n\n"
        f"{lvl_info['emoji']} سطح: <b>{lvl_info['title']}</b>\n"
        f"🔐 احراز هویت: {ver_status}\n💳 کارت KYC: <code>{kf}</code>\n"
        f"🔒 وضعیت: {ban_status}"
    )
    ban_btn = _btn("✅ رفع بن", cb=f"adm_unban_{target_id}") if u["is_banned"] else _btn("🚫 بن کردن", cb=f"adm_ban_{target_id}")
    kyc_btn = _btn("🗑 حذف KYC", cb=f"adm_reset_kyc_{target_id}") if u["is_verified"] else _btn("🪪 بدون احراز هویت", cb="noop")
    kb = _kb(
        [_btn("💚 افزایش موجودی", cb=f"adm_bal_add_{target_id}"),
         _btn("🔴 کاهش موجودی", cb=f"adm_bal_sub_{target_id}")],
        [ban_btn, kyc_btn],
        [_btn("💬 ارسال پیام مستقیم", cb=f"adm_dm_{target_id}")],
        [_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")],
    )
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_adm_ban(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    db_mod.ban_user(target_id)
    u = db_mod.get_user(target_id)
    uname = f"@{u['username']}" if u and u["username"] else str(target_id)
    try:
        await ctx.bot.send_message(target_id,
            "🚫 <b>دسترسی شما به ربات مسدود شده است.</b>\n\nبرای پیگیری با پشتیبانی تماس بگیرید.",
            parse_mode=ParseMode.HTML)
    except Exception:
        pass
    await q.edit_message_text(
        f"🚫 کاربر <b>{uname}</b> بن شد.",
        reply_markup=_kb([_btn("✅ رفع بن", cb=f"adm_unban_{target_id}")],
                          [_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML)


async def cb_adm_unban(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    db_mod.unban_user(target_id)
    u = db_mod.get_user(target_id)
    uname = f"@{u['username']}" if u and u["username"] else str(target_id)
    try:
        await ctx.bot.send_message(target_id,
            "✅ <b>دسترسی شما به ربات بازگردانده شد.</b>", parse_mode=ParseMode.HTML)
    except Exception:
        pass
    await q.edit_message_text(
        f"✅ بن کاربر <b>{uname}</b> برداشته شد.",
        reply_markup=_kb([_btn("🚫 بن مجدد", cb=f"adm_ban_{target_id}")],
                          [_btn("بازگشت", cb="adm_users", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML)


async def cb_adm_reset_kyc(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    u = db_mod.get_user(target_id)
    if not u:
        await q.answer("کاربر یافت نشد!", show_alert=True)
        return
    uname = f"@{u['username']}" if u["username"] else f"#{target_id}"
    with db_mod.db() as conn:
        conn.execute("UPDATE users SET is_verified=0 WHERE user_id=?", (target_id,))
        conn.execute(
            "UPDATE kyc_requests SET status='rejected' WHERE user_id=? AND status='pending'",
            (target_id,)
        )
    try:
        await ctx.bot.send_message(
            target_id,
            "⚠️ <b>احراز هویت شما توسط ادمین ریست شد.</b>\n\n"
            "برای استفاده مجدد از امکانات نیازمند KYC، دوباره احراز هویت کنید.",
            parse_mode=ParseMode.HTML,
        )
    except Exception:
        pass
    await q.edit_message_text(
        f"✅ احراز هویت کاربر <b>{u['full_name']}</b> ({uname}) ریست شد.\n"
        f"وضعیت: ❌ تأیید نشده",
        reply_markup=_kb(
            [_btn("بازگشت به کاربر", cb=f"adm_user_detail_{target_id}")],
            [_btn("🔙 لیست کاربران", cb="adm_users")],
        ),
        parse_mode=ParseMode.HTML,
    )
    log.info("[KYC] ادمین %s — ریست KYC کاربر %s", q.from_user.id, target_id)

async def cb_adm_bal_action(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    parts = q.data.split("_")
    action = parts[2]  
    target_id = int(parts[3])
    u = db_mod.get_user(target_id)
    if not u:
        await q.answer("کاربر یافت نشد!", show_alert=True)
        return
    ctx.user_data["adm_state"] = f"adm_bal_{action}_{target_id}"
    action_label = "افزایش ✅" if action == "add" else "کاهش 🔴"
    uname = f"@{u['username']}" if u["username"] else f"#{target_id}"
    await q.edit_message_text(
        f"💰 <b>مدیریت موجودی — {action_label}</b>\n\n"
        f"👤 کاربر: <b>{u['full_name']}</b> ({uname})\n"
        f"💳 موجودی فعلی: <b>{_fmt_balance(u['balance'])}</b>\n\n"
        "مبلغ مورد نظر را به <b>تومان</b> وارد کنید:\n"
        "مثال: <code>50000</code>\n\n"
        "⚠️ برای کاهش موجودی زیر صفر اجازه داده نمی‌شود.",
        reply_markup=_kb([_btn("انصراف", cb=f"adm_user_detail_{target_id}")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_adm_bal_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_bal_") or not _is_admin(update.effective_user.id):
        return
    parts = state.split("_")
    action = parts[2]
    target_id = int(parts[3])
    ctx.user_data["adm_state"] = None

    raw = update.message.text.strip().replace(",", "").replace("،", "")
    if not raw.isdigit() or int(raw) <= 0:
        await update.message.reply_text(
            "❌ مبلغ باید عدد صحیح مثبت باشد.\nمثال: <code>50000</code>",
            parse_mode=ParseMode.HTML)
        return

    amount = int(raw)
    u = db_mod.get_user(target_id)
    if not u:
        await update.message.reply_text("❌ کاربر یافت نشد.")
        return

    uname = f"@{u['username']}" if u["username"] else f"#{target_id}"

    if action == "add":
        new_balance = db_mod.update_balance(target_id, amount)
        action_txt = f"➕ <b>{_fmt_balance(amount)}</b> به موجودی افزوده شد"
        notif_user = (
            f"💚 <b>موجودی کیف پول شما شارژ شد!</b>\n\n"
            f"💰 مبلغ واریزی: <b>{_fmt_balance(amount)}</b>\n"
            f"💳 موجودی جدید: <b>{_fmt_balance(new_balance)}</b>\n\n"
            f"🕐 <i>{_jalali_now()}</i>"
        )
    else:
        if u["balance"] < amount:
            await update.message.reply_text(
                f"❌ موجودی کافی نیست.\n"
                f"موجودی فعلی: <b>{_fmt_balance(u['balance'])}</b>\n"
                f"مبلغ درخواستی: <b>{_fmt_balance(amount)}</b>",
                parse_mode=ParseMode.HTML,
            )
            return
        new_balance = db_mod.update_balance(target_id, -amount)
        action_txt = f"➖ <b>{_fmt_balance(amount)}</b> از موجودی کسر شد"
        notif_user = (
            f"🔴 <b>از موجودی کیف پول شما کسر شد.</b>\n\n"
            f"💸 مبلغ کسرشده: <b>{_fmt_balance(amount)}</b>\n"
            f"💳 موجودی جدید: <b>{_fmt_balance(new_balance)}</b>\n\n"
            f"🕐 <i>{_jalali_now()}</i>"
        )

    try:
        await ctx.bot.send_message(target_id, notif_user, parse_mode=ParseMode.HTML)
    except Exception:
        pass

    await update.message.reply_text(
        f"✅ <b>عملیات موجودی انجام شد</b>\n\n"
        f"👤 کاربر: <b>{u['full_name']}</b> ({uname})\n"
        f"{action_txt}\n"
        f"💳 موجودی جدید: <b>{_fmt_balance(new_balance)}</b>",
        reply_markup=_kb(
            [_btn("💚 افزایش مجدد", cb=f"adm_bal_add_{target_id}"),
             _btn("🔴 کاهش مجدد", cb=f"adm_bal_sub_{target_id}")],
            [_btn("بازگشت به کاربر", cb=f"adm_user_detail_{target_id}")],
            [_btn("🔙 پنل ادمین", cb="adm_back")],
        ),
        parse_mode=ParseMode.HTML,
    )
    log.info(
        "[موجودی] ادمین %s — %s — کاربر %s — مبلغ %s — موجودی جدید %s",
        update.effective_user.id, action, target_id, amount, new_balance
    )


async def cb_adm_dm(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    u = db_mod.get_user(target_id)
    ctx.user_data["adm_state"] = f"adm_dm_{target_id}"
    await q.edit_message_text(
        f"💬 <b>ارسال پیام به {u['full_name'] if u else target_id}</b>\n"
        f"🆔 شناسه: <code>{target_id}</code>\n\n"
        "پیام خود را بنویسید:\n<i>(کاربر می‌تواند پاسخ دهد)</i>",
        reply_markup=_kb([_btn("انصراف", cb="adm_users", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_dm(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_dm_") or not _is_admin(update.effective_user.id):
        return
    target_id = int(state.split("_")[-1])
    ctx.user_data["adm_state"] = None
    u = db_mod.get_user(target_id)
    try:
        await ctx.bot.send_message(target_id,
            f"📩 <b>پیام از پشتیبانی فروشگاه:</b>\n\n{update.message.text}",
            parse_mode=ParseMode.HTML,
            reply_markup=_kb([_btn("↩️ پاسخ به پشتیبانی", cb="reply_to_support")]))
        await update.message.reply_text(
            f"✅ پیام به <b>{u['full_name'] if u else target_id}</b> ارسال شد.",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)
    except Exception as e:
        await update.message.reply_text(f"❌ ارسال ناموفق: {e}")


async def cb_reply_to_support(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    ctx.user_data["state"] = "awaiting_support_reply"
    try:
        await q.edit_message_reply_markup()
    except Exception:
        pass
    await ctx.bot.send_message(q.from_user.id,
        "✍️ پیام پاسخ خود را بنویسید:",
        reply_markup=_kb([_btn("انصراف", cb="back_main", emoji_id="6030757850274336631")]))


async def handle_support_reply(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("state") != "awaiting_support_reply":
        return
    ctx.user_data["state"] = None
    user = update.effective_user
    msg = update.message.text or ""
    await _notify_admins(
        ctx, "notify_support",
        f"↩️ <b>پاسخ کاربر به پشتیبانی</b>\n\n"
        f"👤 <b>{user.full_name}</b>\n🆔 شناسه: <code>{user.id}</code>\n"
        f"📛 یوزرنیم: @{user.username if user.username else '—'}\n\n📝 پاسخ:\n{msg}",
        _kb([_btn(f"↩️ پاسخ به {user.id}", cb=f"adm_dm_{user.id}")]),
    )
    await update.message.reply_text("✅ پاسخ شما ارسال شد.", reply_markup=MAIN_MENU_KB)

SECTION_LABELS = {
    "shop":         "🛍 فروش محصولات",
    "topup":        "💳 شارژ حساب",
    "topup_card":   "💳 پرداخت کارتی",
    "topup_crypto": "💰 پرداخت ارزی",
    "topup_gateway":"🌐 پرداخت درگاهی",
    "support":      "🎧 پشتیبانی",
    "referral":     "👥 زیرمجموعه‌گیری",
}


async def cb_adm_discounts(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    codes = db_mod.get_all_discount_codes()
    lines = ["🎟 <b>مدیریت کدهای تخفیف</b>\n"]
    for c in codes[:20]:
        active_icon = "✅" if c["is_active"] else "🔴"
        type_txt = f"{c['value']}٪" if c["type"] == "percent" else f"{c['value']:,} تومان"
        uses_txt = f"{c['used_count']}/{c['max_uses']}" if c["max_uses"] > 0 else f"{c['used_count']}/∞"
        lines.append(f"{active_icon} <code>{c['code']}</code> — {type_txt} — استفاده: {uses_txt}")
    text = "\n".join(lines) if len(lines) > 1 else "🎟 <b>کدی ثبت نشده است.</b>"
    kb = _kb(
        [_btn("➕ کد جدید — درصدی", cb="adm_disc_new_percent"),
         _btn("➕ کد جدید — مبلغ ثابت", cb="adm_disc_new_fixed")],
        [_btn("📋 مدیریت کدها", cb="adm_disc_list")],
        [_btn("🔙 پنل ادمین", cb="adm_back")],
    )
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_adm_disc_new(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    disc_type = "percent" if "percent" in q.data else "fixed"
    ctx.user_data["adm_state"] = f"adm_disc_new_{disc_type}"
    type_txt = "درصدی (مثلاً ۱۰ یعنی ۱۰٪)" if disc_type == "percent" else "مبلغ ثابت (تومان)"
    await q.edit_message_text(
        f"🎟 <b>کد تخفیف جدید — {type_txt}</b>\n\n"
        "اطلاعات را به این شکل وارد کنید:\n"
        "<code>کد | مقدار | حداکثر استفاده | تاریخ انقضا</code>\n\n"
        "مثال‌ها:\n"
        "<code>SUMMER10 | 10 | 100 | 2025-12-31</code>\n"
        "<code>VIP50 | 50000 | 0 | -</code>\n\n"
        "• حداکثر استفاده <code>0</code> = نامحدود\n"
        "• تاریخ انقضا <code>-</code> = بدون انقضا",
        reply_markup=_kb([_btn("انصراف", cb="adm_discounts", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_adm_disc_new(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_disc_new_") or not _is_admin(update.effective_user.id):
        return
    disc_type = "percent" if "percent" in state else "fixed"
    ctx.user_data["adm_state"] = None
    parts = [p.strip() for p in update.message.text.split("|")]
    if len(parts) < 2:
        await update.message.reply_text("❌ فرمت اشتباه. دوباره امتحان کنید.",
                                         reply_markup=_kb([_btn("بازگشت", cb="adm_discounts", emoji_id="5346112927688574301")]))
        return
    code = parts[0].upper().strip()
    try:
        value = int(parts[1].strip().replace(",", ""))
        max_uses = int(parts[2].strip()) if len(parts) > 2 and parts[2].strip().isdigit() else 0
        exp_raw = parts[3].strip() if len(parts) > 3 else "-"
        expires_at = None if exp_raw in ("-", "") else f"{exp_raw} 23:59:59"
    except (ValueError, IndexError):
        await update.message.reply_text("❌ مقادیر نامعتبر. دوباره امتحان کنید.",
                                         reply_markup=_kb([_btn("بازگشت", cb="adm_discounts", emoji_id="5346112927688574301")]))
        return
    try:
        code_id = db_mod.create_discount_code(code, disc_type, value, max_uses, expires_at)
    except Exception as e:
        await update.message.reply_text(f"❌ خطا: {e}\n(احتمالاً کد تکراری است)",
                                         reply_markup=_kb([_btn("بازگشت", cb="adm_discounts", emoji_id="5346112927688574301")]))
        return
    type_txt = f"{value}٪ درصد" if disc_type == "percent" else f"{value:,} تومان"
    uses_txt = f"حداکثر {max_uses}" if max_uses > 0 else "نامحدود"
    await update.message.reply_text(
        f"✅ <b>کد تخفیف ساخته شد!</b>\n\n"
        f"🎟 کد: <code>{code}</code>\n"
        f"💸 مقدار: <b>{type_txt}</b>\n"
        f"🔢 استفاده: <b>{uses_txt}</b>\n"
        f"📅 انقضا: <b>{expires_at or 'بدون انقضا'}</b>\n"
        f"🆔 شناسه: <b>#{code_id}</b>",
        reply_markup=_kb([_btn("🎟 مدیریت کدها", cb="adm_discounts"),
                           _btn("🔙 پنل ادمین", cb="adm_back")]),
        parse_mode=ParseMode.HTML,
    )


async def cb_adm_disc_list(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    codes = db_mod.get_all_discount_codes()
    if not codes:
        await q.edit_message_text("🎟 هیچ کدی ثبت نشده.",
                                    reply_markup=_kb([_btn("بازگشت", cb="adm_discounts", emoji_id="5346112927688574301")]))
        return
    rows = []
    for c in codes:
        active_icon = "✅" if c["is_active"] else "🔴"
        rows.append([_btn(f"{active_icon} {c['code']} | #{c['code_id']}", cb=f"adm_disc_detail_{c['code_id']}")])
    rows.append([_btn("بازگشت", cb="adm_discounts", emoji_id="5346112927688574301")])
    await q.edit_message_text("🎟 <b>لیست کدهای تخفیف</b>\nبرای مدیریت روی هر کد کلیک کنید:",
                               reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_disc_detail(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    code_id = int(q.data.split("_")[-1])
    codes = db_mod.get_all_discount_codes()
    c = next((x for x in codes if x["code_id"] == code_id), None)
    if not c:
        await q.answer("یافت نشد!", show_alert=True)
        return
    active_icon = "✅ فعال" if c["is_active"] else "🔴 غیرفعال"
    type_txt = f"{c['value']}٪" if c["type"] == "percent" else f"{c['value']:,} تومان"
    uses_txt = f"{c['used_count']}/{c['max_uses']}" if c["max_uses"] > 0 else f"{c['used_count']}/∞"
    toggle_label = "🔴 غیرفعال کن" if c["is_active"] else "✅ فعال کن"
    await q.edit_message_text(
        f"🎟 <b>کد تخفیف #{code_id}</b>\n\n"
        f"کد: <code>{c['code']}</code>\n"
        f"نوع: <b>{'درصدی' if c['type'] == 'percent' else 'مبلغ ثابت'}</b>\n"
        f"مقدار: <b>{type_txt}</b>\n"
        f"استفاده: <b>{uses_txt}</b>\n"
        f"انقضا: <b>{c['expires_at'] or 'بدون انقضا'}</b>\n"
        f"وضعیت: <b>{active_icon}</b>",
        reply_markup=_kb(
            [_btn(toggle_label, cb=f"adm_disc_toggle_{code_id}"),
             _btn("🗑 حذف کد", cb=f"adm_disc_delete_{code_id}")],
            [_btn("بازگشت", cb="adm_disc_list", emoji_id="5346112927688574301")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def cb_adm_disc_toggle(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    if not _is_admin(q.from_user.id):
        await q.answer()
        return
    code_id = int(q.data.split("_")[-1])
    new_val = db_mod.toggle_discount_code(code_id)
    await q.answer("✅ فعال شد" if new_val else "🔴 غیرفعال شد", show_alert=True)
    await cb_adm_disc_detail(update, ctx)


async def cb_adm_disc_delete(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    code_id = int(q.data.split("_")[-1])
    db_mod.delete_discount_code(code_id)
    await q.edit_message_text(
        f"✅ کد تخفیف <b>#{code_id}</b> حذف شد.",
        reply_markup=_kb([_btn("بازگشت به کدها", cb="adm_discounts", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML,
    )

async def cb_adm_admins(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if q.from_user.id not in config.ADMIN_IDS:
        await q.answer("⛔ فقط ادمین اصلی می‌تواند این بخش را مدیریت کند.", show_alert=True)
        return
    db_admins = db_mod.get_bot_admins_from_db()
    lines = ["👑 <b>مدیریت ادمین‌ها</b>\n"]
    lines.append(f"🔐 <b>ادمین‌های اصلی (config):</b>")
    for aid in config.ADMIN_IDS:
        lines.append(f"  • <code>{aid}</code> 🔒")
    if db_admins:
        lines.append(f"\n➕ <b>ادمین‌های اضافه‌شده:</b>")
        for a in db_admins:
            name = a["full_name"] or str(a["admin_id"])
            uname = f"@{a['username']}" if a["username"] else ""
            lines.append(f"  • <b>{name}</b> {uname} — <code>{a['admin_id']}</code>")
    else:
        lines.append("\n<i>هیچ ادمین اضافه‌شده‌ای وجود ندارد.</i>")
    rows = []
    if db_admins:
        for a in db_admins:
            name = a["full_name"] or str(a["admin_id"])
            rows.append([
                _btn(f"🔔 اعلان‌ها — {name}", cb=f"adm_notif_{a['admin_id']}"),
                _btn(f"🗑 حذف", cb=f"adm_remove_admin_{a['admin_id']}"),
            ])
    rows.append([_btn("➕ افزودن ادمین جدید", cb="adm_add_admin")])
    rows.append([_btn("🔙 پنل ادمین", cb="adm_back")])
    await q.edit_message_text("\n".join(lines), reply_markup=InlineKeyboardMarkup(rows),
                               parse_mode=ParseMode.HTML)


async def cb_adm_add_admin(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if q.from_user.id not in config.ADMIN_IDS:
        await q.answer("⛔ دسترسی ندارید.", show_alert=True)
        return
    ctx.user_data["adm_state"] = "adm_add_admin"
    await q.edit_message_text(
        "👑 <b>افزودن ادمین جدید</b>\n\n"
        "آیدی عددی تلگرام کاربر را وارد کنید:\n"
        "مثال: <code>123456789</code>\n\n"
        "⚠️ کاربر باید قبلاً ربات را استارت زده باشد.",
        reply_markup=_kb([_btn("انصراف", cb="adm_admins", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_adm_add_admin(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "adm_add_admin":
        return
    if update.effective_user.id not in config.ADMIN_IDS:
        return
    ctx.user_data["adm_state"] = None
    raw = update.message.text.strip()
    if not raw.isdigit():
        await update.message.reply_text("❌ آیدی باید عدد باشد.",
                                         reply_markup=_kb([_btn("بازگشت", cb="adm_admins", emoji_id="5346112927688574301")]))
        return
    new_admin_id = int(raw)
    if new_admin_id == update.effective_user.id:
        await update.message.reply_text("❌ خودتان را نمی‌توانید اضافه کنید.")
        return
    u = db_mod.get_user(new_admin_id)
    name = u["full_name"] if u else str(new_admin_id)
    result = db_mod.add_admin(new_admin_id, update.effective_user.id)
    if not result:
        await update.message.reply_text(
            f"⚠️ کاربر <b>{name}</b> (<code>{new_admin_id}</code>) از قبل ادمین است.",
            reply_markup=_kb([_btn("بازگشت", cb="adm_admins", emoji_id="5346112927688574301")]),
            parse_mode=ParseMode.HTML)
        return
    try:
        await update.message.bot.send_message(
            new_admin_id,
            f"👑 <b>تبریک!</b> شما به عنوان ادمین ربات منصوب شدید.\n"
            f"برای دسترسی به پنل ادمین: /admin",
            parse_mode=ParseMode.HTML)
    except Exception:
        pass
    await update.message.reply_text(
        f"✅ کاربر <b>{name}</b> (<code>{new_admin_id}</code>) به عنوان ادمین اضافه شد.",
        reply_markup=_kb([_btn("👑 مدیریت ادمین‌ها", cb="adm_admins"),
                           _btn("🔙 پنل ادمین", cb="adm_back")]),
        parse_mode=ParseMode.HTML)
    log.info("[ادمین] %s — ادمین جدید %s اضافه شد", update.effective_user.id, new_admin_id)


async def cb_adm_remove_admin(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if q.from_user.id not in config.ADMIN_IDS:
        await q.answer("⛔ دسترسی ندارید.", show_alert=True)
        return
    target_id = int(q.data.split("_")[-1])
    u = db_mod.get_user(target_id)
    name = u["full_name"] if u else str(target_id)
    result = db_mod.remove_admin(target_id)
    if not result:
        await q.answer("⚠️ این ادمین قابل حذف نیست (ادمین اصلی).", show_alert=True)
        return
    try:
        await q.bot.send_message(target_id,
            "⚠️ دسترسی ادمین شما در ربات لغو شد.", parse_mode=ParseMode.HTML)
    except Exception:
        pass
    await q.edit_message_text(
        f"✅ ادمین <b>{name}</b> (<code>{target_id}</code>) حذف شد.",
        reply_markup=_kb([_btn("بازگشت به ادمین‌ها", cb="adm_admins", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML)
    log.info("[ادمین] %s — ادمین %s حذف شد", q.from_user.id, target_id)

async def cb_adm_sections(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    rows = []
    for sec_key, label in SECTION_LABELS.items():
        active = db_mod.is_section_active(sec_key)
        icon = "✅" if active else "🔴"
        status = "فعال" if active else "غیرفعال"
        rows.append([_btn(f"{icon} {label}: {status}", cb=f"adm_toggle_section_{sec_key}")])
    rows.append([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        "⚙️ <b>کنترل بخش‌های ربات</b>\n\n"
        "روی هر بخش کلیک کنید تا وضعیت آن تغییر کند:\n✅ = فعال  |  🔴 = غیرفعال",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_toggle_section(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    if not _is_admin(q.from_user.id):
        await q.answer()
        return
    prefix = "adm_toggle_section_"
    sec_key = q.data[len(prefix):]
    new_state = db_mod.toggle_section(sec_key)
    label = SECTION_LABELS.get(sec_key, sec_key)
    status_txt = "✅ فعال شد" if new_state else "🔴 غیرفعال شد"
    await q.answer(f"{label} {status_txt}", show_alert=True)
    await cb_adm_sections(update, ctx)

async def cb_adm_kyc_list(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    with db_mod.db() as conn:
        rows = conn.execute(
            "SELECT k.*, u.full_name, u.username FROM kyc_requests k "
            "JOIN users u ON k.user_id=u.user_id "
            "WHERE k.status='pending' ORDER BY k.kyc_id DESC LIMIT 20"
        ).fetchall()
    if not rows:
        await q.edit_message_text("✅ هیچ درخواست KYC در انتظاری وجود ندارد.",
                                   reply_markup=_kb([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")]))
        return
    btn_rows = []
    for r in rows:
        uname = f"@{r['username']}" if r["username"] else str(r["user_id"])
        btn_rows.append([_btn(f"#{r['kyc_id']} — {r['full_name']} ({uname})",
                               cb=f"adm_kyc_view_{r['kyc_id']}")])
    btn_rows.append([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        f"🪪 <b>درخواست‌های KYC در انتظار ({len(rows)})</b>",
        reply_markup=InlineKeyboardMarkup(btn_rows), parse_mode=ParseMode.HTML)


async def cb_adm_kyc_view(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    kyc_id = int(q.data.split("_")[-1])
    req = db_mod.get_kyc_request(kyc_id)
    if not req:
        await q.answer("درخواست یافت نشد!", show_alert=True)
        return
    user_row = db_mod.get_user(req["user_id"])
    uname = f"@{user_row['username']}" if user_row and user_row["username"] else "—"
    caption = (
        f"🪪 <b>درخواست KYC #{kyc_id}</b>\n\n"
        f"👤 نام: <b>{user_row['full_name'] if user_row else req['user_id']}</b>\n"
        f"🆔 شناسه: <code>{req['user_id']}</code>\n📛 یوزرنیم: {uname}\n"
        f"📅 تاریخ: {req['created_at'][:16]}\n\n"
        "برای تأیید، آخرین ۴ رقم کارت بانکی کاربر را وارد کنید، سپس دکمه تأیید را بزنید."
    )
    kb = _kb([_btn(f"✅ تأیید KYC #{kyc_id}", cb=f"approve_kyc_{kyc_id}"),
              _btn(f"❌ رد KYC #{kyc_id}", cb=f"reject_kyc_{kyc_id}")],
             [_btn("بازگشت", cb="adm_kyc_list", emoji_id="5346112927688574301")])
    try:
        await ctx.bot.send_photo(q.from_user.id, photo=req["photo_file_id"], caption=caption,
                                  reply_markup=kb, parse_mode=ParseMode.HTML)
        await q.answer()
    except Exception:
        await q.edit_message_text(caption, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_approve_kyc(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    kyc_id = int(q.data.split("_")[-1])
    ctx.user_data["adm_state"] = f"adm_kyc_card4_{kyc_id}"
    try:
        await q.edit_message_caption(
            caption=(q.message.caption or "") + "\n\n✅ آخرین ۴ رقم کارت بانکی کاربر را وارد کنید:\nمثال: <code>1234</code>",
            reply_markup=_kb([_btn("انصراف", cb="adm_kyc_list", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)
    except Exception:
        await ctx.bot.send_message(q.from_user.id,
            f"✅ آخرین ۴ رقم کارت کاربر برای KYC #{kyc_id} را وارد کنید:\nمثال: <code>1234</code>",
            reply_markup=_kb([_btn("انصراف", cb="adm_kyc_list", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_kyc_card4(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_kyc_card4_") or not _is_admin(update.effective_user.id):
        return
    kyc_id = int(state.split("_")[-1])
    card4 = update.message.text.strip()
    ctx.user_data["adm_state"] = None
    if not card4.isdigit() or len(card4) != 4:
        await update.message.reply_text("❌ باید دقیقاً ۴ رقم وارد کنید.\nمثال: <code>1234</code>",
                                         parse_mode=ParseMode.HTML)
        ctx.user_data["adm_state"] = f"adm_kyc_card4_{kyc_id}"
        return
    with db_mod.db() as conn:
        conn.execute("UPDATE kyc_requests SET card_last4=? WHERE kyc_id=?", (card4, kyc_id))
    db_mod.approve_kyc(kyc_id)
    req = db_mod.get_kyc_request(kyc_id)
    user_row = db_mod.get_user(req["user_id"]) if req else None
    await update.message.reply_text(
        f"✅ <b>KYC #{kyc_id} تأیید شد!</b>\n\n"
        f"👤 کاربر: <b>{user_row['full_name'] if user_row else '—'}</b>\n"
        f"💳 آخرین ۴ رقم کارت: <b>{card4}</b>",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)
    if req:
        try:
            await ctx.bot.send_message(req["user_id"],
                f"🎉 <b>احراز هویت شما تأیید شد!</b>\n\n"
                f"✅ از این پس می‌توانید از تمام امکانات ربات استفاده کنید.\n"
                f"💳 واریز کارت به کارت فقط با کارت ثبت‌شده (آخر ۴ رقم: <b>{card4}</b>) مقدور است.",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
        except Exception:
            pass


async def cb_reject_kyc(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    kyc_id = int(q.data.split("_")[-1])
    db_mod.reject_kyc(kyc_id)
    req = db_mod.get_kyc_request(kyc_id)
    try:
        await q.edit_message_caption(
            caption=(q.message.caption or "") + f"\n\n❌ <b>KYC #{kyc_id} رد شد.</b>",
            parse_mode=ParseMode.HTML)
    except Exception:
        await q.answer(f"KYC #{kyc_id} رد شد.", show_alert=True)
    if req:
        try:
            await ctx.bot.send_message(req["user_id"],
                "❌ <b>درخواست احراز هویت شما رد شد.</b>\n\nبرای پیگیری با پشتیبانی تماس بگیرید.",
                reply_markup=MAIN_MENU_KB, parse_mode=ParseMode.HTML)
        except Exception:
            pass

async def cb_adm_fee_settings(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    await q.edit_message_text(
        f"💰 <b>تنظیم کارمزد</b>\n\n"
        f"📱 کارمزد ثابت پریمیوم: <b>{_fmt_balance(_get_premium_fee())}</b>\n"
        f"⭐ کارمزد درصدی استارز: <b>{_get_stars_fee_pct():.1f}%</b>\n\n"
        "کدام کارمزد را تغییر می‌دهید؟",
        reply_markup=_kb(
            [_btn("📱 کارمزد پریمیوم (ثابت)", cb="adm_set_premium_fee")],
            [_btn("⭐ کارمزد استارز (درصد)", cb="adm_set_stars_fee")],
            [_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")],
        ), parse_mode=ParseMode.HTML)


async def cb_adm_set_premium_fee(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_set_premium_fee"
    await q.edit_message_text(
        f"📱 <b>تنظیم کارمزد ثابت پریمیوم</b>\n\nمقدار فعلی: <b>{_fmt_balance(_get_premium_fee())}</b>\n\n"
        "مبلغ جدید کارمزد را به تومان وارد کنید:\nمثال: <code>50000</code>",
        reply_markup=_kb([_btn("انصراف", cb="adm_fee_settings", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def cb_adm_set_stars_fee(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_set_stars_fee"
    await q.edit_message_text(
        f"⭐ <b>تنظیم کارمزد درصدی استارز</b>\n\nمقدار فعلی: <b>{_get_stars_fee_pct():.1f}%</b>\n\n"
        "درصد جدید کارمزد را وارد کنید:\nمثال: <code>5</code>",
        reply_markup=_kb([_btn("انصراف", cb="adm_fee_settings", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def cb_adm_kyc_threshold(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_set_kyc_threshold"
    await q.edit_message_text(
        f"🔑 <b>تنظیم آستانه احراز هویت</b>\n\nمقدار فعلی: <b>{_fmt_balance(_get_kyc_threshold())}</b>\n\n"
        "خریدها بیشتر از این مبلغ نیاز به KYC دارند.\n\n"
        "مبلغ جدید را به تومان وارد کنید:\nمثال: <code>350000</code>",
        reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_fee_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not _is_admin(update.effective_user.id):
        return
    text = update.message.text.strip().replace(",", "")
    if state == "adm_set_premium_fee":
        if not text.isdigit():
            await update.message.reply_text("❌ مبلغ باید عدد صحیح تومانی باشد.")
            return
        db_mod.set_setting("premium_fee", text)
        ctx.user_data["adm_state"] = None
        await update.message.reply_text(
            f"✅ کارمزد ثابت پریمیوم به <b>{int(text):,} تومان</b> تغییر یافت.",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)
    elif state == "adm_set_stars_fee":
        try:
            pct = float(text)
            if pct < 0 or pct > 100:
                raise ValueError
        except ValueError:
            await update.message.reply_text("❌ درصد باید عددی بین ۰ تا ۱۰۰ باشد.")
            return
        db_mod.set_setting("stars_fee_pct", str(pct))
        ctx.user_data["adm_state"] = None
        await update.message.reply_text(
            f"✅ کارمزد درصدی استارز به <b>{pct:.1f}%</b> تغییر یافت.",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)
    elif state == "adm_set_kyc_threshold":
        if not text.isdigit():
            await update.message.reply_text("❌ مبلغ باید عدد صحیح تومانی باشد.")
            return
        db_mod.set_setting("kyc_threshold", text)
        ctx.user_data["adm_state"] = None
        await update.message.reply_text(
            f"✅ آستانه احراز هویت به <b>{int(text):,} تومان</b> تغییر یافت.",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)

async def cb_adm_manage_products(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cats = db_mod.get_categories(active_only=False)
    if not cats:
        await q.edit_message_text("⚠️ هیچ دسته‌بندی‌ای وجود ندارد.",
                                   reply_markup=_kb([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")]))
        return
    rows = []
    for c in cats:
        status = "✅" if c["is_active"] else "🗑️"
        rows.append([_btn(f"{status} {c['emoji']} {c['name']}", cb=f"adm_cat_manage_{c['cat_id']}"),
                     _btn("🗑 حذف", cb=f"adm_del_cat_{c['cat_id']}")])
    has_inactive = any(not c["is_active"] for c in cats)
    if has_inactive:
        rows.append([_btn("🧹 پاکسازی دسته‌های غیرفعال قدیمی", cb="adm_purge_softdeleted")])
    rows.append([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")])
    await q.edit_message_text("🗂 <b>مدیریت محصولات</b>\n\nدسته‌بندی مورد نظر را انتخاب کنید:",
                               reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_purge_softdeleted(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    result = db_mod.purge_soft_deleted()
    await q.edit_message_text(
        f"🧹 <b>پاکسازی کامل شد</b>\n\n"
        f"🗂 دسته‌های حذف‌شده: <b>{result['deleted_cats']}</b>\n"
        f"📦 محصولات حذف‌شده: <b>{result['deleted_products']}</b>",
        reply_markup=_kb([_btn("بازگشت به مدیریت محصولات", cb="adm_manage_products", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML,
    )
    log.info("[پاکسازی] ادمین %s — %s دسته، %s محصول حذف شد",
             q.from_user.id, result["deleted_cats"], result["deleted_products"])


async def cb_adm_cat_manage(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cat_id = int(q.data.split("_")[-1])
    cat = db_mod.get_category(cat_id)
    products = db_mod.get_products_by_category(cat_id)
    if not products:
        await q.edit_message_text(
            f"📂 دسته <b>{cat['emoji']} {cat['name']}</b>\n\nمحصولی وجود ندارد.",
            reply_markup=_kb([_btn("➕ افزودن محصول", cb="adm_add_product")],
                              [_btn("بازگشت", cb="adm_manage_products", emoji_id="5346112927688574301")]),
            parse_mode=ParseMode.HTML)
        return
    rows = []
    usd_irt = await _fetch_usd_to_irt()
    for p in products:
        price_irt = int(p["price_usd"] * usd_irt)
        rows.append([_btn(f"✏️ {p['name']} | {price_irt:,}تومان", cb=f"adm_edit_product_{p['product_id']}"),
                     _btn("🗑", cb=f"adm_del_product_{p['product_id']}")])
    rows.append([_btn("بازگشت", cb="adm_manage_products", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        f"📂 <b>{cat['emoji']} {cat['name']}</b>\n\nمحصول مورد نظر را انتخاب کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_del_product(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    product_id = int(q.data.split("_")[-1])
    p = db_mod.get_product(product_id)
    if not p:
        await q.answer("محصول یافت نشد!", show_alert=True)
        return
    await q.edit_message_text(
        f"🗑 <b>حذف محصول</b>\n\nآیا مطمئن هستید که «<b>{p['name']}</b>» را حذف کنید؟",
        reply_markup=_kb([_btn("✅ بله، حذف کن", cb=f"adm_confirm_del_product_{product_id}"),
                           _btn("انصراف", cb=f"adm_cat_manage_{p['cat_id']}")]),
        parse_mode=ParseMode.HTML)


async def cb_adm_confirm_del_product(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    product_id = int(q.data.split("_")[-1])
    p = db_mod.get_product(product_id)
    cat_id = p["cat_id"] if p else None
    p_name = p["name"] if p else str(product_id)
    try:
        db_mod.delete_product(product_id)
        await q.edit_message_text(
            f"✅ محصول «<b>{p_name}</b>» کاملاً حذف شد.",
            reply_markup=_kb(
                [_btn("بازگشت به دسته",
                      cb=f"adm_cat_manage_{cat_id}" if cat_id else "adm_manage_products",
                      emoji_id=EMOJI_BACK)],
                [_btn("مدیریت محصولات", cb="adm_manage_products")],
            ),
            parse_mode=ParseMode.HTML)
        log.info("[حذف محصول] ادمین %s — product_id=%s — %s",
                 q.from_user.id, product_id, p_name)
    except Exception as e:
        log.error("[حذف محصول] خطا: %s", e)
        await q.answer(f"❌ خطا در حذف: {e}", show_alert=True)


async def cb_adm_del_cat(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cat_id = int(q.data.split("_")[-1])
    cat = db_mod.get_category(cat_id)
    if not cat:
        await q.answer("دسته یافت نشد!", show_alert=True)
        return
    with db_mod.db() as conn:
        prod_count = conn.execute(
            "SELECT COUNT(*) as c FROM products WHERE cat_id=?", (cat_id,)
        ).fetchone()["c"]
    prod_txt = f"و <b>{prod_count} محصول</b> داخل آن" if prod_count > 0 else "بدون محصول"
    await q.edit_message_text(
        f"🗑 <b>حذف کامل دسته‌بندی</b>\n\n"
        f"دسته «<b>{cat['emoji']} {cat['name']}</b>» {prod_txt} <b>برای همیشه</b> حذف خواهد شد.\n\n"
        "⚠️ این عملیات برگشت‌پذیر نیست!",
        reply_markup=_kb([_btn("🗑 بله، کاملاً حذف کن", cb=f"adm_confirm_del_cat_{cat_id}"),
                           _btn("انصراف", cb="adm_manage_products", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML)


async def cb_adm_confirm_del_cat(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cat_id = int(q.data.split("_")[-1])
    cat = db_mod.get_category(cat_id)
    cat_name = f"{cat['emoji']} {cat['name']}" if cat else str(cat_id)
    try:
        deleted_products = db_mod.delete_category(cat_id)
        prod_txt = f" (همراه با {deleted_products} محصول)" if deleted_products > 0 else ""
        await q.edit_message_text(
            f"✅ دسته «<b>{cat_name}</b>»{prod_txt} کاملاً حذف شد.",
            reply_markup=_kb([_btn("بازگشت به مدیریت محصولات",
                                   cb="adm_manage_products",
                                   emoji_id=EMOJI_BACK)]),
            parse_mode=ParseMode.HTML)
        log.info("[حذف دسته] ادمین %s — cat_id=%s — %s",
                 q.from_user.id, cat_id, cat_name)
    except Exception as e:
        log.error("[حذف دسته] خطا: %s", e)
        await q.answer(f"❌ خطا در حذف: {e}", show_alert=True)


async def cb_adm_edit_product(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    product_id = int(q.data.split("_")[-1])
    p = db_mod.get_product(product_id)
    if not p:
        await q.answer("محصول یافت نشد!", show_alert=True)
        return
    usd_irt = await _fetch_usd_to_irt()
    price_irt = int(p["price_usd"] * usd_irt)
    text = (
        f"✏️ <b>ویرایش محصول</b>\n\n"
        f"📝 نام: <b>{p['name']}</b>\n"
        f"📄 توضیحات: {p['description'][:60]}{'...' if len(p['description']) > 60 else ''}\n"
        f"💵 قیمت: <b>{p['price_usd']} دلار | {price_irt:,} تومان</b>\n"
        f"📦 موجودی: {'نامحدود' if p['stock'] == -1 else p['stock']}"
    )
    kb = _kb(
        [_btn("✏️ ویرایش نام", cb=f"adm_edit_name_{product_id}"),
         _btn("📄 ویرایش توضیحات", cb=f"adm_edit_desc_{product_id}")],
        [_btn("💵 ویرایش قیمت", cb=f"adm_edit_price_{product_id}"),
         _btn("📦 ویرایش موجودی", cb=f"adm_edit_stock_{product_id}")],
        [_btn("🗑 حذف محصول", cb=f"adm_del_product_{product_id}")],
        [_btn("بازگشت", cb=f"adm_cat_manage_{p['cat_id']}")],
    )
    await q.edit_message_text(text, reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_adm_edit_field(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    parts = q.data.split("_")
    field = parts[2]
    product_id = int(parts[3])
    prompts = {
        "name":  "نام جدید محصول را وارد کنید:",
        "desc":  "توضیحات جدید را وارد کنید:",
        "price": "قیمت جدید را به دلار وارد کنید:\nمثال: <code>4.99</code>",
        "stock": "موجودی جدید را وارد کنید:\n<code>-1</code> نامحدود | <code>0</code> ناموجود | عدد مثبت",
    }
    ctx.user_data["adm_state"] = f"adm_edit_product_{field}_{product_id}"
    ctx.user_data["edit_product_id"] = product_id
    ctx.user_data["edit_field"] = field
    await q.edit_message_text(
        f"✏️ <b>ویرایش محصول</b>\n\n{prompts.get(field, 'مقدار جدید را وارد کنید:')}",
        reply_markup=_kb([_btn("انصراف", cb=f"adm_edit_product_{product_id}")]),
        parse_mode=ParseMode.HTML)


async def handle_adm_edit_product_field(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_edit_product_") or not _is_admin(update.effective_user.id):
        return
    product_id = ctx.user_data.get("edit_product_id")
    field = ctx.user_data.get("edit_field")
    new_val = update.message.text.strip()
    ctx.user_data["adm_state"] = None
    if not product_id or not field:
        await update.message.reply_text("❌ خطا. دوباره تلاش کنید.")
        return
    with db_mod.db() as conn:
        if field == "name":
            conn.execute("UPDATE products SET name=? WHERE product_id=?", (new_val, product_id))
        elif field == "desc":
            conn.execute("UPDATE products SET description=? WHERE product_id=?", (new_val, product_id))
        elif field == "price":
            try:
                price = float(new_val)
            except ValueError:
                await update.message.reply_text("❌ قیمت باید عدد باشد.")
                ctx.user_data["adm_state"] = f"adm_edit_product_price_{product_id}"
                return
            conn.execute("UPDATE products SET price_usd=? WHERE product_id=?", (price, product_id))
        elif field == "stock":
            try:
                stock = int(new_val)
            except ValueError:
                await update.message.reply_text("❌ موجودی باید عدد صحیح باشد.")
                ctx.user_data["adm_state"] = f"adm_edit_product_stock_{product_id}"
                return
            conn.execute("UPDATE products SET stock=? WHERE product_id=?", (stock, product_id))
    await update.message.reply_text(
        "✅ محصول بروزرسانی شد.",
        reply_markup=_kb([_btn("✏️ ادامه ویرایش", cb=f"adm_edit_product_{product_id}")],
                          [_btn("🔙 پنل ادمین", cb="adm_back")]))


async def cb_adm_add_cat(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "awaiting_cat_info"
    await q.edit_message_text(
        "📂 <b>افزودن دسته‌بندی جدید</b>\n\n"
        "نام و ایموجی دسته را به این فرمت وارد کنید:\n<code>ایموجی نام دسته</code>\n\n"
        "مثال: <code>🎮 بازی‌های دیجیتال</code>",
        reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_cat_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "awaiting_cat_info" or not _is_admin(update.effective_user.id):
        return
    raw = update.message.text.strip()
    parts = raw.split(None, 1)
    def _is_emoji(s: str) -> bool:
        if not s:
            return False
        return unicodedata.category(s[0]) in ("So", "Sm", "Sk") or ord(s[0]) > 0x1F000

    if len(parts) >= 2 and _is_emoji(parts[0]):
        emoji_ch, name = parts[0], parts[1].strip()
    else:
        emoji_ch = "📦"
        name = raw
    if len(name) < 2:
        await update.message.reply_text(
            "❌ نام دسته باید حداقل ۲ کاراکتر باشد.\n"
            "مثال: <code>📱 اشتراک‌های دیجیتال</code>",
            parse_mode=ParseMode.HTML)
        return
    ctx.user_data["adm_state"] = None
    try:
        cat_id = db_mod.add_category(name, emoji_ch)
    except Exception as e:
        if "UNIQUE" in str(e):
            await update.message.reply_text(
                f"❌ دسته‌بندی با نام «<b>{name}</b>» از قبل وجود دارد.",
                parse_mode=ParseMode.HTML)
        else:
            await update.message.reply_text(f"❌ خطا: {e}")
        return
    await update.message.reply_text(
        f"✅ دسته‌بندی «{emoji_ch} <b>{name}</b>» با شناسه <b>#{cat_id}</b> اضافه شد.",
        reply_markup=_kb(
            [_btn("➕ افزودن محصول به این دسته", cb="adm_add_product")],
            [_btn("📂 افزودن دسته جدید", cb="adm_add_cat")],
            [_btn("🔙 پنل ادمین", cb="adm_back")],
        ),
        parse_mode=ParseMode.HTML)

async def cb_adm_add_product(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cats = db_mod.get_categories()
    if not cats:
        await q.edit_message_text("❌ ابتدا یک دسته‌بندی اضافه کنید.",
                                   reply_markup=_kb([_btn("📂 افزودن دسته‌بندی", cb="adm_add_cat")],
                                                    [_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")]))
        return
    ctx.user_data["adm_state"] = "adm_product_name"
    ctx.user_data["new_product"] = {}
    await q.edit_message_text(
        "➕ <b>افزودن محصول جدید</b>\n\nمرحله <b>۱ از ۴</b>\n\n📝 <b>نام محصول</b> را وارد کنید:",
        reply_markup=_kb([_btn("📖 راهنمای محصولات", cb="adm_guide")],
                          [_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML)


async def handle_adm_product_steps(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_product") or not _is_admin(update.effective_user.id):
        return
    text = update.message.text.strip()
    np = ctx.user_data.setdefault("new_product", {})

    if state == "adm_product_name":
        if len(text) < 3:
            await update.message.reply_text("❌ نام محصول باید حداقل ۳ کاراکتر باشد.")
            return
        np["name"] = text
        ptype = _detect_product_type(text, "")
        ptype_hints = {
            PRODUCT_TYPE_TELEGRAM_PREMIUM: "📱 شناسایی شد: <b>تلگرام پریمیوم</b> ✅",
            PRODUCT_TYPE_TELEGRAM_STARS:   "⭐ شناسایی شد: <b>استارز تلگرام</b> ✅",
            PRODUCT_TYPE_CHATGPT:          "🤖 شناسایی شد: <b>ChatGPT</b> ✅",
            PRODUCT_TYPE_VIRTUAL_NUMBER:   "📞 شناسایی شد: <b>شماره مجازی</b> ✅",
            PRODUCT_TYPE_BOOST:            "🚀 شناسایی شد: <b>بوست کانال</b> ✅",
            PRODUCT_TYPE_GENERIC:          "📦 نوع: <b>محصول عمومی</b>",
        }
        ctx.user_data["adm_state"] = "adm_product_desc"
        await update.message.reply_text(
            f"✅ نام ثبت شد: <b>{text}</b>\n{ptype_hints.get(ptype,'')}\n\n"
            "مرحله <b>۲ از ۴</b>\n\n📄 <b>توضیحات محصول</b> را وارد کنید:",
            reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)

    elif state == "adm_product_desc":
        if len(text) < 5:
            await update.message.reply_text("❌ توضیحات باید حداقل ۵ کاراکتر باشد.")
            return
        np["description"] = text
        ctx.user_data["adm_state"] = "adm_product_price"
        await update.message.reply_text(
            "مرحله <b>۳ از ۴</b>\n\n💵 <b>قیمت محصول به دلار</b> را وارد کنید:\nمثال: <code>2.99</code>",
            reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)

    elif state == "adm_product_price":
        try:
            price_usd = float(text.replace(",", ""))
            if price_usd <= 0:
                raise ValueError
        except ValueError:
            await update.message.reply_text("❌ قیمت باید یک عدد مثبت باشد.\nمثال: <code>2.99</code>",
                                             parse_mode=ParseMode.HTML)
            return
        np["price_usd"] = price_usd
        ctx.user_data["adm_state"] = "adm_product_stock"
        usd_irt = await _fetch_usd_to_irt()
        await update.message.reply_text(
            f"✅ قیمت: <b>{price_usd} دلار ≈ {int(price_usd*usd_irt):,} تومان</b>\n\n"
            "مرحله <b>۳.۵ از ۴</b>\n\n📦 <b>موجودی:</b>\n"
            "<code>-1</code> نامحدود | <code>0</code> ناموجود | عدد مثبت",
            reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)

    elif state == "adm_product_stock":
        try:
            stock = int(text)
        except ValueError:
            await update.message.reply_text("❌ موجودی باید عدد صحیح باشد.")
            return
        np["stock"] = stock
        ctx.user_data["adm_state"] = "adm_product_cat"
        cats = db_mod.get_categories()
        rows = [[_btn(f"{c['emoji']} {c['name']}", cb=f"adm_pcat_{c['cat_id']}")] for c in cats]
        rows.append([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")])
        stock_display = "نامحدود ♾" if stock == -1 else ("ناموجود ❌" if stock == 0 else f"{stock} عدد")
        await update.message.reply_text(
            f"مرحله <b>۴ از ۴</b>\n\n📝 نام: <b>{np['name']}</b>\n"
            f"💵 قیمت: <b>{np['price_usd']} دلار</b>\n📦 موجودی: <b>{stock_display}</b>\n\n"
            "📂 <b>دسته‌بندی</b> را انتخاب کنید:",
            reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_product_cat(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    cat_id = int(q.data.split("_")[-1])
    np = ctx.user_data.get("new_product", {})
    if not all(k in np for k in ("name", "description", "price_usd")):
        await q.edit_message_text("❌ اطلاعات محصول ناقص است.", reply_markup=ADMIN_MENU_KB)
        return
    stock = np.get("stock", -1)
    product_id = db_mod.add_product(cat_id, np["name"], np["description"], np["price_usd"], stock)
    ctx.user_data["adm_state"] = None
    ctx.user_data["new_product"] = {}
    cat = db_mod.get_category(cat_id)
    usd_irt = await _fetch_usd_to_irt()
    price_irt = int(np["price_usd"] * usd_irt)
    stock_display = "نامحدود ♾" if stock == -1 else ("ناموجود ❌" if stock == 0 else f"{stock} عدد")
    await q.edit_message_text(
        f"✅ <b>محصول با موفقیت اضافه شد!</b>\n\n🆔 شناسه: <b>#{product_id}</b>\n"
        f"📝 نام: <b>{np['name']}</b>\n📂 دسته: {cat['emoji']} {cat['name']}\n"
        f"💵 قیمت: <b>{np['price_usd']} دلار | {price_irt:,} تومان</b>\n"
        f"📦 موجودی: <b>{stock_display}</b>",
        reply_markup=_kb([_btn("➕ افزودن محصول دیگر", cb="adm_add_product")],
                          [_btn("🔙 پنل ادمین", cb="adm_back")]),
        parse_mode=ParseMode.HTML)

async def cb_adm_wallets(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    rows = []
    for key, info in config.CRYPTO_DEPOSIT_OPTIONS.items():
        wallet = db_mod.get_setting(info["wallet_key"]) or "—"
        short = wallet[:12] + "…" if len(wallet) > 12 else wallet
        rows.append([_btn(
            f"{info['emoji']} {info['name']} ({info['network']}) — {short}",
            cb=f"adm_set_wallet_{key}",
        )])
    rows.append([_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")])
    await q.edit_message_text(
        "💼 <b>تنظیم آدرس ولت‌های رمز ارز</b>\n\n"
        "تتر BEP20، تتر TRC20، تون، ترون TRC20 — فقط آدرس ولت را وارد کنید:",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML,
    )


async def cb_adm_gateway(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    title = db_mod.get_setting("gateway_title") or "پرداخت درگاهی"
    instr = db_mod.get_setting("gateway_instructions") or ""
    await q.edit_message_text(
        f"🌐 <b>تنظیم پرداخت درگاهی</b>\n\n"
        f"عنوان فعلی: <b>{title}</b>\n"
        f"راهنما:\n{instr}\n\n"
        "چه موردی را ویرایش می‌کنید؟",
        reply_markup=_kb(
            [_btn("✏️ عنوان", cb="adm_gateway_title")],
            [_btn("✏️ متن راهنما", cb="adm_gateway_instr")],
            [_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")],
        ),
        parse_mode=ParseMode.HTML,
    )


async def cb_adm_set_wallet(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    sym = q.data.replace("adm_set_wallet_", "", 1)
    ctx.user_data["adm_state"] = f"adm_wallet_{sym}"
    ctx.user_data["adm_wallet_sym"] = sym
    info = config.CRYPTO_DEPOSIT_OPTIONS.get(sym, {})
    current = db_mod.get_setting(info.get("wallet_key", f"wallet_{sym}")) or "تنظیم نشده"
    await q.edit_message_text(
        f"💼 <b>ولت {info.get('name', sym)} — {info.get('network', '')}</b>\n\n"
        f"آدرس فعلی: <code>{current}</code>\n\nآدرس جدید را وارد کنید:",
        reply_markup=_kb([_btn("بازگشت", cb="adm_wallets", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML,
    )


async def cb_adm_activate_crypto(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    sym = q.data.split("_")[-1]
    db_mod.set_setting("active_crypto", sym)
    info = config.SUPPORTED_CRYPTOS.get(sym, {})
    await q.edit_message_text(f"✅ ارز فعال به <b>{info.get('name', sym)}</b> تغییر یافت.",
                               reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)


async def handle_adm_wallet_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_wallet_") or not _is_admin(update.effective_user.id):
        return
    sym = ctx.user_data.get("adm_wallet_sym")
    new_wallet = update.message.text.strip()
    info = config.CRYPTO_DEPOSIT_OPTIONS.get(sym, {})
    wallet_key = info.get("wallet_key", f"wallet_{sym}")
    db_mod.set_setting(wallet_key, new_wallet)
    ctx.user_data["adm_state"] = None
    await update.message.reply_text(
        f"✅ آدرس ولت <b>{info.get('name', sym)}</b> ({info.get('network', '')}) ذخیره شد:\n"
        f"<code>{new_wallet}</code>",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML,
    )


async def cb_adm_set_network(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    sym = q.data.split("_")[-1]
    net_info = CRYPTO_NETWORK_INFO.get(sym, {})
    networks = net_info.get("networks", [])
    _, current_network = _resolved_deposit_network(sym)
    if not networks:
        await q.answer("شبکه‌ای برای این ارز تعریف نشده!", show_alert=True)
        return
    rows = []
    for i, n in enumerate(networks):
        mark = "✅ " if n == current_network else ""
        rows.append([_btn(f"{mark}{n}", cb=f"adm_pick_network_{sym}_{i}")])
    rows.append([_btn("بازگشت", cb="adm_wallets", emoji_id="5346112927688574301")])
    info = config.SUPPORTED_CRYPTOS.get(sym, {})
    await q.edit_message_text(
        f"🌐 <b>تنظیم شبکه واریز {info.get('name', sym)}</b>\n\nشبکه فعلی: <b>{current_network or 'پیش‌فرض'}</b>",
        reply_markup=InlineKeyboardMarkup(rows), parse_mode=ParseMode.HTML)


async def cb_adm_pick_network(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    parts = q.data.split("_")
    idx = int(parts[-1])
    sym = parts[-2]
    networks = CRYPTO_NETWORK_INFO.get(sym, {}).get("networks", [])
    if idx >= len(networks):
        await q.answer("خطا!", show_alert=True)
        return
    chosen = networks[idx]
    db_mod.set_setting(f"deposit_network_{sym}", chosen)
    info = config.SUPPORTED_CRYPTOS.get(sym, {})
    await q.edit_message_text(
        f"✅ شبکه واریز <b>{info.get('name', sym)}</b> به <b>{chosen}</b> تغییر یافت.",
        reply_markup=_kb([_btn("بازگشت به ولت‌ها", cb="adm_wallets", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML)


async def cb_adm_gateway_field(
    update: Update, ctx: ContextTypes.DEFAULT_TYPE, field: str,
) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = f"adm_gateway_{field}"
    prompt = "عنوان درگاه را وارد کنید:" if field == "title" else "متن راهنمای درگاه را وارد کنید:"
    await q.edit_message_text(
        f"🌐 <b>ویرایش درگاه</b>\n\n{prompt}",
        reply_markup=_kb([_btn("انصراف", cb="adm_gateway", emoji_id="6030757850274336631")]),
        parse_mode=ParseMode.HTML,
    )


async def handle_adm_gateway_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_gateway_") or not _is_admin(update.effective_user.id):
        return
    field = state.replace("adm_gateway_", "")
    text = update.message.text.strip()
    ctx.user_data["adm_state"] = None
    if field == "title":
        db_mod.set_setting("gateway_title", text)
    else:
        db_mod.set_setting("gateway_instructions", text)
    await update.message.reply_text("✅ ذخیره شد.", reply_markup=ADMIN_MENU_KB)

async def cb_adm_card(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    current_card = db_mod.get_setting("card_number") or "تنظیم نشده"
    current_holder = db_mod.get_setting("card_holder") or "تنظیم نشده"
    ctx.user_data["adm_state"] = "adm_card_number"
    await q.edit_message_text(
        f"💳 <b>تنظیم کارت بانکی</b>\n\n"
        f"کارت فعلی: <code>{current_card}</code>\n"
        f"نام صاحب کارت: <b>{current_holder}</b>\n\nشماره کارت جدید را وارد کنید (۱۶ رقم):",
        reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_card_input(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not _is_admin(update.effective_user.id):
        return
    text = update.message.text.strip()
    if state == "adm_card_number":
        card = text.replace("-", "").replace(" ", "")
        if not card.isdigit() or len(card) != 16:
            await update.message.reply_text("❌ شماره کارت باید دقیقاً ۱۶ رقم باشد.")
            return
        db_mod.set_setting("card_number", card)
        ctx.user_data["adm_state"] = "adm_card_holder"
        await update.message.reply_text("✅ شماره کارت ذخیره شد.\n\nنام صاحب کارت را وارد کنید:")
    elif state == "adm_card_holder":
        db_mod.set_setting("card_holder", text)
        ctx.user_data["adm_state"] = None
        card_num = db_mod.get_setting("card_number")
        card_fmt = "-".join([card_num[i:i+4] for i in range(0, 16, 4)])
        await update.message.reply_text(
            f"✅ اطلاعات کارت ذخیره شد:\n💳 شماره: <code>{card_fmt}</code>\n👤 نام: <b>{text}</b>",
            reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)

async def cb_adm_forced_channel(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    current = db_mod.get_setting("forced_channel") or "تنظیم نشده"
    ctx.user_data["adm_state"] = "adm_forced_channel"
    await q.edit_message_text(
        f"🔒 <b>تنظیم کانال جوین اجباری</b>\n\nکانال فعلی: <b>{current}</b>\n\n"
        "آیدی کانال جدید را وارد کنید:\nمثال: <code>@mychannel</code>\n\n"
        "برای غیرفعال کردن، عدد <code>0</code> وارد کنید:",
        reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_forced_channel(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "adm_forced_channel" or not _is_admin(update.effective_user.id):
        return
    val = update.message.text.strip()
    db_mod.set_setting("forced_channel", "" if val == "0" else val)
    ctx.user_data["adm_state"] = None
    status = f"کانال «<b>{val}</b>» فعال شد." if val != "0" else "جوین اجباری <b>غیرفعال</b> شد."
    await update.message.reply_text(f"✅ {status}", reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)

async def cb_adm_broadcast(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_broadcast_msg"
    await q.edit_message_text("📢 <b>ارسال پیام همگانی</b>\n\nپیام خود را بنویسید (HTML پشتیبانی می‌شود):",
                               reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]),
                               parse_mode=ParseMode.HTML)


async def handle_adm_broadcast(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "adm_broadcast_msg" or not _is_admin(update.effective_user.id):
        return
    ctx.user_data["adm_state"] = None
    msg_text = update.message.text
    user_ids = db_mod.get_all_user_ids()
    await update.message.reply_text(f"⏳ در حال ارسال به <b>{len(user_ids)}</b> کاربر...", parse_mode=ParseMode.HTML)
    sent, failed = 0, 0
    for uid in user_ids:
        try:
            await ctx.bot.send_message(uid, msg_text, parse_mode=ParseMode.HTML)
            sent += 1
        except Exception:
            failed += 1
        await asyncio.sleep(0.05)
    await update.message.reply_text(
        f"✅ ارسال پایان یافت.\n✅ موفق: <b>{sent}</b>\n❌ ناموفق: <b>{failed}</b>",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)


async def cb_adm_forward(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    ctx.user_data["adm_state"] = "adm_forward_msg"
    await q.edit_message_text("↪️ <b>فوروارد همگانی</b>\n\nپیامی که می‌خواهید فوروارد شود را فوروارد کنید:",
                               reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]),
                               parse_mode=ParseMode.HTML)


async def handle_adm_forward(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if ctx.user_data.get("adm_state") != "adm_forward_msg" or not _is_admin(update.effective_user.id):
        return
    ctx.user_data["adm_state"] = None
    user_ids = db_mod.get_all_user_ids()
    await update.message.reply_text(f"⏳ در حال فوروارد به <b>{len(user_ids)}</b> کاربر...", parse_mode=ParseMode.HTML)
    sent, failed = 0, 0
    for uid in user_ids:
        try:
            await update.message.forward(uid)
            sent += 1
        except Exception:
            failed += 1
        await asyncio.sleep(0.05)
    await update.message.reply_text(
        f"✅ فوروارد پایان یافت.\n✅ موفق: <b>{sent}</b>\n❌ ناموفق: <b>{failed}</b>",
        reply_markup=ADMIN_MENU_KB, parse_mode=ParseMode.HTML)

async def cb_reply_user(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    target_id = int(q.data.split("_")[-1])
    ctx.user_data["adm_state"] = f"adm_reply_{target_id}"
    await q.edit_message_text(
        f"↩️ <b>پاسخ به کاربر {target_id}</b>\n\nپیام خود را بنویسید:",
        reply_markup=_kb([_btn("انصراف", cb="adm_back", emoji_id="6030757850274336631")]), parse_mode=ParseMode.HTML)


async def handle_adm_reply(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    state = ctx.user_data.get("adm_state", "")
    if not state.startswith("adm_reply_") or not _is_admin(update.effective_user.id):
        return
    target_id = int(state.split("_")[-1])
    ctx.user_data["adm_state"] = None
    try:
        await ctx.bot.send_message(target_id,
            f"📩 <b>پیام از پشتیبانی:</b>\n\n{update.message.text}",
            parse_mode=ParseMode.HTML,
            reply_markup=_kb([_btn("↩️ پاسخ به پشتیبانی", cb="reply_to_support")]))
        await update.message.reply_text("✅ پاسخ ارسال شد.", reply_markup=ADMIN_MENU_KB)
    except Exception:
        await update.message.reply_text("❌ ارسال پیام ناموفق بود.")

async def cb_adm_guide(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    kb = _kb(
        [_btn("📱 پریمیوم تلگرام", cb="guide_premium"), _btn("⭐ استارز تلگرام", cb="guide_stars")],
        [_btn("🤖 ChatGPT", cb="guide_chatgpt"), _btn("📞 شماره مجازی", cb="guide_vnumber")],
        [_btn("🚀 بوست کانال", cb="guide_boost"), _btn("📦 محصول عمومی", cb="guide_generic")],
        [_btn("بازگشت", cb="adm_back", emoji_id="5346112927688574301")],
    )
    await q.edit_message_text("📖 <b>راهنمای افزودن محصولات</b>",
                               reply_markup=kb, parse_mode=ParseMode.HTML)


async def cb_adm_guide_detail(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    await q.answer()
    if not _is_admin(q.from_user.id):
        return
    guide_type = q.data.split("_")[-1]
    guides = {
        "premium": "📱 <b>تلگرام پریمیوم</b>\n\nنامی حاوی «پریمیوم تلگرام» یا «Telegram Premium» وارد کنید.\nمثال: <code>تلگرام پریمیوم ۱ ماهه</code>",
        "stars":   "⭐ <b>استارز تلگرام</b>\n\nنامی حاوی «استارز تلگرام» یا «Telegram Stars» وارد کنید.\nمثال: <code>۱۰۰ استارز تلگرام</code>",
        "chatgpt": "🤖 <b>ChatGPT</b>\n\nنامی حاوی «ChatGPT» یا «چت جی پی تی» وارد کنید.\nمثال: <code>اکانت ChatGPT Plus یک ماهه</code>",
        "vnumber": "📞 <b>شماره مجازی</b>\n\nنامی حاوی «شماره مجازی» یا «Virtual Number» وارد کنید.\nمثال: <code>شماره مجازی ایران — واتساپ</code>",
        "boost":   "🚀 <b>بوست کانال</b>\n\nنام باید شامل «بوست» یا «boost» باشد.\nمثال: <code>بوست کانال ۵ عددی</code>",
        "generic": "📦 <b>محصول عمومی</b>\n\nهیچ اطلاعات خاصی از کاربر پرسیده نمی‌شود.\nمحتوای تحویل را هنگام تایید سفارش وارد کنید.",
    }
    await q.edit_message_text(
        guides.get(guide_type, "راهنمایی برای این نوع محصول موجود نیست."),
        reply_markup=_kb([_btn("➕ افزودن محصول الان", cb="adm_add_product")],
                          [_btn("بازگشت", cb="adm_guide", emoji_id="5346112927688574301")]),
        parse_mode=ParseMode.HTML)

async def handle_text(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if await _check_banned(update):
        return

    adm_state = ctx.user_data.get("adm_state", "")
    user_state = ctx.user_data.get("state", "")

    if _is_admin(update.effective_user.id) and adm_state:
        if adm_state == "adm_broadcast_msg":
            await handle_adm_broadcast(update, ctx)
        elif adm_state == "adm_forward_msg":
            await handle_adm_forward(update, ctx)
        elif adm_state == "awaiting_cat_info":
            await handle_adm_cat_input(update, ctx)
        elif adm_state.startswith("adm_product"):
            await handle_adm_product_steps(update, ctx)
        elif adm_state.startswith("adm_wallet_"):
            await handle_adm_wallet_input(update, ctx)
        elif adm_state in ("adm_card_number", "adm_card_holder"):
            await handle_adm_card_input(update, ctx)
        elif adm_state == "adm_forced_channel":
            await handle_adm_forced_channel(update, ctx)
        elif adm_state.startswith("adm_reply_"):
            await handle_adm_reply(update, ctx)
        elif adm_state.startswith("adm_deliver_"):
            await handle_adm_deliver_order(update, ctx)
        elif adm_state.startswith("adm_dep_amount_"):
            await handle_adm_dep_amount(update, ctx)
        elif adm_state.startswith("adm_edit_product_"):
            await handle_adm_edit_product_field(update, ctx)
        elif adm_state.startswith("adm_kyc_card4_"):
            await handle_adm_kyc_card4(update, ctx)
        elif adm_state in ("adm_set_premium_fee", "adm_set_stars_fee", "adm_set_kyc_threshold"):
            await handle_adm_fee_input(update, ctx)
        elif adm_state in ("adm_gateway_title", "adm_gateway_instr"):
            await handle_adm_gateway_input(update, ctx)
        elif adm_state == "adm_search_user":
            await handle_adm_search_user_input(update, ctx)
        elif adm_state.startswith("adm_dm_"):
            await handle_adm_dm(update, ctx)
        elif adm_state.startswith("adm_bal_"):
            await handle_adm_bal_input(update, ctx)
        elif adm_state.startswith("adm_disc_new_"):
            await handle_adm_disc_new(update, ctx)
        elif adm_state == "adm_add_admin":
            await handle_adm_add_admin(update, ctx)
        return

    if user_state == "awaiting_order_extra_info":
        await handle_order_extra_info(update, ctx)
    elif user_state.startswith("awaiting_qty_"):
        await handle_qty_input(update, ctx)
    elif user_state.startswith("awaiting_discount_"):
        await handle_discount_input(update, ctx)
    elif user_state == "awaiting_order_id":
        await handle_order_track(update, ctx)
    elif user_state == "awaiting_support_msg":
        await handle_support_msg(update, ctx)
    elif user_state == "awaiting_support_reply":
        await handle_support_reply(update, ctx)
    elif user_state == "awaiting_card_deposit_amount":
        await handle_card_deposit_amount(update, ctx)
    elif user_state == "awaiting_crypto_deposit_amount":
        await handle_crypto_deposit_amount(update, ctx)
    elif user_state == "awaiting_gateway_deposit_amount":
        await handle_gateway_deposit_amount(update, ctx)
    elif user_state == "awaiting_kyc_card_number":
        await handle_kyc_card_number(update, ctx)
    elif user_state == "awaiting_user_discount_code":
        await handle_user_discount_input(update, ctx)


async def handle_photo(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if await _check_banned(update):
        return
    user_state = ctx.user_data.get("state", "")
    if user_state == "awaiting_deposit_photo":
        await handle_deposit_photo(update, ctx)
    elif user_state == "awaiting_kyc_photo":
        await handle_kyc_photo(update, ctx)

async def cb_router(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    q = update.callback_query
    data = q.data or ""

    if data.startswith("adm_confirm_order_") or data.startswith("ao_"):
        await cb_adm_confirm_order(update, ctx)
        return
    if data.startswith("adm_reject_order_") or data.startswith("ar_"):
        await cb_adm_reject_order(update, ctx)
        return

    if data == "noop":
        await q.answer()
        return

    if data == "back_main":
        await cmd_start(update, ctx)
    elif data == "menu_products":
        await cb_products_menu(update, ctx)
    elif data == "menu_account":
        await cb_account(update, ctx)
    elif data == "kyc_request":
        await cb_kyc_request(update, ctx)
    elif data == "kyc_proceed_voluntary":
        await cb_kyc_proceed_voluntary(update, ctx)
    elif data == "kyc_proceed_deposit":
        await cb_kyc_proceed_deposit(update, ctx)
    elif data.startswith("kyc_proceed_"):
        await cb_kyc_proceed(update, ctx)
    elif data == "my_referral_link":
        await cb_my_referral_link(update, ctx)
    elif data == "user_discount_code":
        await cb_user_discount_code(update, ctx)
    elif data == "add_to_group":
        await cb_add_to_group(update, ctx)
    elif data == "menu_topup":
        await cb_topup_menu(update, ctx)
    elif data == "topup_crypto":
        await cb_topup_crypto(update, ctx)
    elif data == "topup_card":
        await cb_topup_card(update, ctx)
    elif data == "topup_gateway":
        await cb_topup_gateway(update, ctx)
    elif data == "checkout_pay_card":
        await cb_checkout_pay(update, ctx, "card")
    elif data == "checkout_pay_crypto":
        await cb_checkout_pay(update, ctx, "crypto")
    elif data == "checkout_pay_gateway":
        await cb_checkout_pay(update, ctx, "gateway")
    elif data == "confirm_username_yes":
        await cb_confirm_username_yes(update, ctx)
    elif data.startswith("confirm_username_no_"):
        await cb_confirm_username_no(update, ctx)
    elif data.startswith("edit_username_"):
        await cb_edit_username(update, ctx)
    elif data.startswith("crypto_pick_"):
        await cb_crypto_pick(update, ctx)
    elif data == "crypto_send_receipt":
        await cb_crypto_send_receipt(update, ctx)
    elif data == "menu_referral":
        await cb_referral(update, ctx)
    elif data == "menu_orders":
        await cb_orders(update, ctx)
    elif data == "menu_track":
        await cb_track_menu(update, ctx)
    elif data == "menu_support":
        await cb_support(update, ctx)
    elif data == "reply_to_support":
        await cb_reply_to_support(update, ctx)
    elif data == "check_join":
        if await _check_forced_join(ctx.bot, q.from_user.id):
            await cmd_start(update, ctx)
        else:
            await q.answer("❌ هنوز عضو نشده‌اید!", show_alert=True)

    elif data == "price_list":
        await cb_price_list(update, ctx)
    elif data.startswith("cat_"):
        await cb_category(update, ctx)
    elif data.startswith("product_"):
        await cb_product_detail(update, ctx)
    elif data.startswith("buy_"):
        await cb_buy_product(update, ctx)
    elif data.startswith("order_for_myself_"):
        await cb_order_for_myself(update, ctx)

    elif data == "adm_back":
        if _is_admin(q.from_user.id):
            ctx.user_data["adm_state"] = None
            await q.edit_message_text("👑 <b>پنل مدیریت</b>", reply_markup=ADMIN_MENU_KB,
                                       parse_mode=ParseMode.HTML)
    elif data == "adm_pending_orders":
        await cb_adm_pending_orders(update, ctx)
    elif data.startswith("adm_pending_view_"):
        await cb_adm_pending_view(update, ctx)
    elif data == "adm_stats":
        await cb_adm_stats(update, ctx)
    elif data == "adm_gateway":
        await cb_adm_gateway(update, ctx)
    elif data == "adm_gateway_title":
        await cb_adm_gateway_field(update, ctx, "title")
    elif data == "adm_gateway_instr":
        await cb_adm_gateway_field(update, ctx, "instr")
    elif data.startswith("adm_notif_"):
        await cb_adm_notif_settings(update, ctx)
    elif data.startswith("adm_toggle_notif_"):
        await cb_adm_toggle_notif(update, ctx)
    elif data == "adm_users":
        await cb_adm_users(update, ctx)
    elif data == "adm_search_user":
        await cb_adm_search_user(update, ctx)
    elif data.startswith("adm_user_list_"):
        await cb_adm_user_list(update, ctx)
    elif data.startswith("adm_user_detail_"):
        await cb_adm_user_detail(update, ctx)
    elif data.startswith("adm_ban_"):
        await cb_adm_ban(update, ctx)
    elif data.startswith("adm_unban_"):
        await cb_adm_unban(update, ctx)
    elif data.startswith("adm_reset_kyc_"):
        await cb_adm_reset_kyc(update, ctx)
    elif data.startswith("adm_bal_add_") or data.startswith("adm_bal_sub_"):
        await cb_adm_bal_action(update, ctx)
    elif data.startswith("adm_dm_"):
        await cb_adm_dm(update, ctx)
    elif data == "adm_sections":
        await cb_adm_sections(update, ctx)
    elif data.startswith("adm_toggle_section_"):
        await cb_adm_toggle_section(update, ctx)
    elif data == "adm_broadcast":
        await cb_adm_broadcast(update, ctx)
    elif data == "adm_forward":
        await cb_adm_forward(update, ctx)
    elif data == "adm_add_cat":
        await cb_adm_add_cat(update, ctx)
    elif data == "adm_add_product":
        await cb_adm_add_product(update, ctx)
    elif data == "adm_manage_products":
        await cb_adm_manage_products(update, ctx)
    elif data == "adm_purge_softdeleted":
        await cb_adm_purge_softdeleted(update, ctx)
    elif data.startswith("adm_cat_manage_"):
        await cb_adm_cat_manage(update, ctx)
    elif data.startswith("adm_del_product_"):
        await cb_adm_del_product(update, ctx)
    elif data.startswith("adm_confirm_del_product_"):
        await cb_adm_confirm_del_product(update, ctx)
    elif data.startswith("adm_del_cat_"):
        await cb_adm_del_cat(update, ctx)
    elif data.startswith("adm_confirm_del_cat_"):
        await cb_adm_confirm_del_cat(update, ctx)
    elif data.startswith("adm_edit_product_"):
        await cb_adm_edit_product(update, ctx)
    elif (data.startswith("adm_edit_name_") or data.startswith("adm_edit_desc_")
          or data.startswith("adm_edit_price_") or data.startswith("adm_edit_stock_")):
        await cb_adm_edit_field(update, ctx)
    elif data.startswith("adm_pcat_"):
        await cb_adm_product_cat(update, ctx)
    elif data == "adm_wallets":
        await cb_adm_wallets(update, ctx)
    elif data.startswith("adm_set_wallet_"):
        await cb_adm_set_wallet(update, ctx)
    elif data.startswith("adm_set_network_"):
        await cb_adm_set_network(update, ctx)
    elif data.startswith("adm_pick_network_"):
        await cb_adm_pick_network(update, ctx)
    elif data.startswith("adm_activate_"):
        await cb_adm_activate_crypto(update, ctx)
    elif data == "adm_card":
        await cb_adm_card(update, ctx)
    elif data == "adm_forced_channel":
        await cb_adm_forced_channel(update, ctx)
    elif data == "adm_guide":
        await cb_adm_guide(update, ctx)
    elif data.startswith("guide_"):
        await cb_adm_guide_detail(update, ctx)
    elif data.startswith("confirm_dep_"):
        await cb_confirm_deposit(update, ctx)
    elif data.startswith("reject_dep_"):
        await cb_reject_deposit(update, ctx)
    elif data.startswith("reply_user_"):
        await cb_reply_user(update, ctx)
    elif data == "adm_kyc_list":
        await cb_adm_kyc_list(update, ctx)
    elif data.startswith("adm_kyc_view_"):
        await cb_adm_kyc_view(update, ctx)
    elif data.startswith("approve_kyc_"):
        await cb_approve_kyc(update, ctx)
    elif data.startswith("reject_kyc_"):
        await cb_reject_kyc(update, ctx)
    elif data.startswith("qty_input_"):
        await cb_qty_input(update, ctx)
    elif data.startswith("apply_discount_"):
        await cb_apply_discount(update, ctx)
    elif data == "adm_discounts":
        await cb_adm_discounts(update, ctx)
    elif data in ("adm_disc_new_percent", "adm_disc_new_fixed"):
        await cb_adm_disc_new(update, ctx)
    elif data == "adm_disc_list":
        await cb_adm_disc_list(update, ctx)
    elif data.startswith("adm_disc_detail_"):
        await cb_adm_disc_detail(update, ctx)
    elif data.startswith("adm_disc_toggle_"):
        await cb_adm_disc_toggle(update, ctx)
    elif data.startswith("adm_disc_delete_"):
        await cb_adm_disc_delete(update, ctx)
    elif data == "adm_admins":
        await cb_adm_admins(update, ctx)
    elif data == "adm_add_admin":
        await cb_adm_add_admin(update, ctx)
    elif data.startswith("adm_remove_admin_"):
        await cb_adm_remove_admin(update, ctx)
    elif data == "adm_fee_settings":
        await cb_adm_fee_settings(update, ctx)
    elif data == "adm_set_premium_fee":
        await cb_adm_set_premium_fee(update, ctx)
    elif data == "adm_set_stars_fee":
        await cb_adm_set_stars_fee(update, ctx)
    elif data == "adm_kyc_threshold":
        await cb_adm_kyc_threshold(update, ctx)
    else:
        await q.answer("دکمه ناشناخته", show_alert=False)

async def handle_slash_comments(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None:
    if not update.message or not update.message.text:
        return
    m = re.match(r"^/(trust|support)(?:@\w+)?\s*$", update.message.text.strip(), re.IGNORECASE)
    if not m:
        return
    cmd = m.group(1).lower()
    if cmd == "trust":
        await update.message.reply_text(TRUST_COMMENT_TEXT)
    elif cmd == "support":
        await update.message.reply_text(SUPPORT_COMMENT_TEXT)


def main() -> None:
    db_mod.init_db()
    log.info("دیتابیس آماده شد.")
    app = Application.builder().token(config.BOT_TOKEN).build()

    app.add_handler(CommandHandler("start", cmd_start))
    app.add_handler(CommandHandler("admin", cmd_admin))
    app.add_handler(MessageHandler(
        filters.TEXT & filters.Regex(re.compile(r"^/(trust|support)", re.IGNORECASE)),
        handle_slash_comments,
    ))
    app.add_handler(CallbackQueryHandler(
        cb_adm_confirm_order,
        pattern=r"^(adm_confirm_order_|ao_)\d+$",
    ))
    app.add_handler(CallbackQueryHandler(
        cb_adm_reject_order,
        pattern=r"^(adm_reject_order_|ar_)\d+$",
    ))
    app.add_handler(CallbackQueryHandler(cb_router))
    app.add_handler(MessageHandler(filters.PHOTO, handle_photo))

    app.add_handler(MessageHandler(
        filters.StatusUpdate.NEW_CHAT_MEMBERS
        & (filters.ChatType.GROUP | filters.ChatType.SUPERGROUP),
        handle_bot_added_to_group,
    ))

    app.add_handler(MessageHandler(
        filters.TEXT & filters.Regex(re.compile(r"^/(trust|support)", re.IGNORECASE))
        & (filters.ChatType.GROUP | filters.ChatType.SUPERGROUP),
        handle_slash_comments,
    ))
    app.add_handler(MessageHandler(
        filters.TEXT & ~filters.COMMAND
        & (filters.ChatType.GROUP | filters.ChatType.SUPERGROUP),
        handle_group_price,
    ))

    # ── هندلر پیام‌های خصوصی — state machine ─────────────────────────────────
    app.add_handler(MessageHandler(
        filters.TEXT & ~filters.COMMAND & filters.ChatType.PRIVATE,
        handle_text,
    ))

    log.info("ربات در حال اجرا است...")
    app.run_polling(drop_pending_updates=True, allowed_updates=Update.ALL_TYPES)


if __name__ == "__main__":
    main()