# handlers/admin/actions.py

from __future__ import annotations

import asyncio
import logging
import platform
import sqlite3
import time
from datetime import datetime
from decimal import Decimal, InvalidOperation
from html import escape
from pathlib import Path
from typing import Any, Optional

from aiogram import F, Router
from aiogram.exceptions import (
    TelegramBadRequest,
    TelegramForbiddenError,
)
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.types import (
    CallbackQuery,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    Message,
)
from aiogram.utils.keyboard import InlineKeyboardBuilder

from core.config import settings
from core.database import DatabaseManager
from keyboards.admin_kb import (
    get_admin_broadcast_options_keyboard,
    get_admin_main_menu,
    get_admin_orders_keyboard,
    get_admin_pricing_keyboard,
    get_admin_server_status_keyboard,
    get_admin_stats_keyboard,
    get_admin_user_manage_keyboard,
    get_admin_wallet_adjust_keyboard,
)
from services.product_pricing_service import (
    PREMIUM_PRODUCT_IDS,
    PRODUCT_LABELS,
    STARS_PRODUCT_IDS,
    InvalidProductPrice,
    ProductPricingService,
)
from utils.finance import finance_manager
from utils.order_datetime import (
    format_order_datetime,
    order_event_time,
)


logger = logging.getLogger(__name__)

router = Router(name="admin_actions")

STARTED_AT = time.monotonic()


# ============================================================
# States
# ============================================================

class AdminActionStates(StatesGroup):
    waiting_rate_value = State()
    waiting_product_usdt_price = State()

    waiting_user_search = State()
    waiting_wallet_user = State()
    waiting_wallet_amount = State()

    waiting_broadcast_text = State()

    waiting_direct_message = State()

    waiting_kyc_reject_reason = State()


# ============================================================
# Authorization
# ============================================================

def _admin_ids() -> frozenset[int]:
    method = getattr(
        settings,
        "get_admin_list",
        None,
    )

    if callable(method):
        try:
            return frozenset(
                int(item)
                for item in method()
                if int(item) > 0
            )

        except Exception:
            logger.exception(
                "Could not read configured admin IDs."
            )

    return frozenset()


def _is_admin(
    user_id: Optional[int],
) -> bool:
    if user_id is None:
        return False

    try:
        value = int(user_id)

    except (
        TypeError,
        ValueError,
    ):
        return False

    return value in _admin_ids()


async def _guard_callback(
    callback: CallbackQuery,
) -> bool:
    user_id = (
        callback.from_user.id
        if callback.from_user
        else None
    )

    if _is_admin(user_id):
        return True

    try:
        await callback.answer(
            "⛔ دسترسی مدیریت ندارید.",
            show_alert=True,
        )
    except Exception:
        pass

    logger.warning(
        "Unauthorized admin callback | "
        "user=%s data=%s",
        user_id,
        callback.data,
    )

    return False


async def _guard_message(
    message: Message,
) -> bool:
    user_id = (
        message.from_user.id
        if message.from_user
        else None
    )

    if _is_admin(user_id):
        return True

    logger.warning(
        "Unauthorized admin state message | user=%s",
        user_id,
    )

    return False


# ============================================================
# UI Helpers
# ============================================================

def _back_admin_keyboard() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(
                    text="بازگشت به پنل مدیریت",
                    callback_data="admin_main",
                    style="danger",
                )
            ]
        ]
    )


def _back_and_refresh_keyboard(
    refresh_callback: str,
) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(
                    text="بروزرسانی",
                    callback_data=refresh_callback,
                    style="primary",
                )
            ],
            [
                InlineKeyboardButton(
                    text="بازگشت به پنل مدیریت",
                    callback_data="admin_main",
                    style="danger",
                )
            ],
        ]
    )


async def _safe_answer(
    callback: CallbackQuery,
    text: Optional[str] = None,
    *,
    show_alert: bool = False,
) -> None:
    try:
        await callback.answer(
            text=text,
            show_alert=show_alert,
        )

    except Exception:
        pass


async def _safe_edit(
    callback: CallbackQuery,
    text: str,
    reply_markup: Optional[
        InlineKeyboardMarkup
    ] = None,
) -> None:
    if not callback.message:
        return

    try:
        await callback.message.edit_text(
            text=text,
            reply_markup=reply_markup,
            parse_mode="HTML",
        )

    except TelegramBadRequest as exc:
        error = str(exc).lower()

        if "message is not modified" in error:
            return

        # Media/caption/old message: send a fresh admin message.
        try:
            await callback.message.answer(
                text=text,
                reply_markup=reply_markup,
                parse_mode="HTML",
            )

        except Exception:
            logger.exception(
                "Could not render admin page."
            )

    except Exception:
        logger.exception(
            "Could not edit admin page."
        )


def _fmt_int(
    value: Any,
) -> str:
    try:
        return f"{int(value or 0):,}"
    except (
        TypeError,
        ValueError,
    ):
        return "0"


def _parse_positive_int(
    value: str,
) -> Optional[int]:
    raw = (
        str(value or "")
        .strip()
        .replace(",", "")
        .replace("٬", "")
        .replace("_", "")
        .replace(" ", "")
    )

    if not raw.isdigit():
        return None

    result = int(raw)

    if result <= 0:
        return None

    return result


def _db_path(
    db: DatabaseManager,
) -> str:
    return str(
        getattr(
            db,
            "db_path",
            getattr(
                db,
                "database_path",
                getattr(
                    settings,
                    "DB_PATH",
                    "matrix_bot.db",
                ),
            ),
        )
    )


# ============================================================
# SQLite helpers for admin-only reports
# ============================================================

def _sqlite_rows(
    db_path: str,
    query: str,
    params: tuple[Any, ...] = (),
) -> list[dict[str, Any]]:
    conn = sqlite3.connect(
        db_path,
        timeout=30,
    )
    conn.row_factory = sqlite3.Row

    try:
        return [
            dict(row)
            for row
            in conn.execute(
                query,
                params,
            ).fetchall()
        ]

    finally:
        conn.close()


def _sqlite_one(
    db_path: str,
    query: str,
    params: tuple[Any, ...] = (),
) -> Optional[dict[str, Any]]:
    rows = _sqlite_rows(
        db_path,
        query,
        params,
    )

    return (
        rows[0]
        if rows
        else None
    )


# ============================================================
# Statistics
# ============================================================

async def _statistics_text(
    db: DatabaseManager,
) -> str:
    stats = await db.get_statistics()

    return (
        "📊 <b>آمار و گزارشات مالی</b>\n"
        "━━━━━━━━━━━━━━━━━━━━\n\n"

        f"👥 کاربران: <code>{_fmt_int(stats.get('users'))}</code>\n"
        f"📦 کل سفارش‌ها: <code>{_fmt_int(stats.get('orders'))}</code>\n"
        f"✅ سفارش تکمیل‌شده: "
        f"<code>{_fmt_int(stats.get('completed_orders'))}</code>\n\n"

        f"💵 فروش تکمیل‌شده: "
        f"<code>{_fmt_int(stats.get('revenue_toman'))}</code> تومان\n"

        f"💳 مجموع موجودی کاربران: "
        f"<code>{_fmt_int(stats.get('wallet_balance_toman'))}</code> تومان\n\n"

        f"⏳ منتظر تایید مالک: "
        f"<code>{_fmt_int(stats.get('pending_owner_orders'))}</code>\n"

        f"⚠️ وضعیت نامشخص Provider: "
        f"<code>{_fmt_int(stats.get('provider_unknown_orders'))}</code>"
    )


@router.callback_query(F.data == "admin_stats")
async def admin_stats(
    callback: CallbackQuery,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    await _safe_edit(
        callback,
        await _statistics_text(db),
        get_admin_stats_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.in_(
        {
            "admin_stats_users",
            "admin_stats_orders",
            "admin_stats_finance",
            "admin_stats_transactions",
            "admin_stats_daily",
        }
    )
)
async def admin_stats_detail(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    base = await _statistics_text(
        db
    )

    suffix = ""

    if callback.data == "admin_stats_daily":
        path = _db_path(db)

        row = await asyncio.to_thread(
            _sqlite_one,
            path,
            """
            SELECT
                COUNT(*) AS orders_count,
                COALESCE(SUM(price), 0) AS revenue
            FROM orders
            WHERE status = 'completed'
              AND DATE(created_at) = DATE('now', 'localtime')
            """,
        )

        suffix = (
            "\n\n📅 <b>فروش امروز</b>\n"
            f"سفارش: <code>{_fmt_int((row or {}).get('orders_count'))}</code>\n"
            f"مبلغ: <code>{_fmt_int((row or {}).get('revenue'))}</code> تومان"
        )

    elif callback.data == "admin_stats_transactions":
        path = _db_path(db)

        row = await asyncio.to_thread(
            _sqlite_one,
            path,
            """
            SELECT
                COUNT(*) AS tx_count,
                COALESCE(SUM(
                    CASE
                        WHEN type = 'credit'
                        THEN amount
                        ELSE 0
                    END
                ), 0) AS credits,
                COALESCE(SUM(
                    CASE
                        WHEN type = 'debit'
                        THEN amount
                        ELSE 0
                    END
                ), 0) AS debits
            FROM wallet_transactions
            """,
        )

        suffix = (
            "\n\n💳 <b>تراکنش‌های کیف پول</b>\n"
            f"تعداد: <code>{_fmt_int((row or {}).get('tx_count'))}</code>\n"
            f"واریز: <code>{_fmt_int((row or {}).get('credits'))}</code> تومان\n"
            f"برداشت: <code>{_fmt_int((row or {}).get('debits'))}</code> تومان"
        )

    await _safe_edit(
        callback,
        base + suffix,
        get_admin_stats_keyboard(),
    )

    await _safe_answer(
        callback
    )


# ============================================================
# Pricing
# ============================================================

RATE_CONFIG: dict[str, tuple[str, str]] = {
    "set_rate_usdt":
        (
            "usdt",
            "نرخ تتر به تومان (Fallback دستی)",
        ),

    "set_rate_ton":
        (
            "ton",
            "نرخ TON/GRAM به تومان (Fallback دستی)",
        ),
}



def _finance_value(
    key: str,
) -> str:
    try:
        value = finance_manager.get_rate(
            key
        )

        if value is None:
            return "تنظیم نشده"

        return (
            f"{finance_manager.format_currency(value)}"
        )

    except Exception:
        logger.exception(
            "Could not read finance rate %s",
            key,
        )
        return "خطا"


def _pricing_text() -> str:
    return (
        "⚙️ <b>تنظیم نرخ و قیمت فروشگاه</b>\n"
        "━━━━━━━━━━━━━━━━━━━━\n\n"

        "💵 USDT/Toman فعلی: "
        f"<code>{escape(_finance_value('usdt'))}</code> تومان\n"

        "🔷 TON/GRAM Toman فعلی: "
        f"<code>{escape(_finance_value('ton'))}</code> تومان\n\n"

        "💎 قیمت پایه Premium: "
        "<b>USDT و جداگانه برای هر پکیج</b>\n"

        "⭐ قیمت پایه Stars: "
        "<b>فقط قیمت هر ۱ Star به USDT</b>\n\n"

        "📌 قیمت نهایی کاربر = قیمت پایه USDT × نرخ زنده USDT/Toman\n"
        "📌 نرخ تتر با Fallback چند صرافی دریافت می‌شود.\n"
        "📌 قیمت محصول دیگر از MarketApp یا Scraping گرفته نمی‌شود."
    )



@router.callback_query(F.data == "admin_pricing")
async def admin_pricing(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    await _safe_edit(
        callback,
        _pricing_text(),
        get_admin_pricing_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.in_(
        set(
            RATE_CONFIG.keys()
        )
    )
)
async def admin_set_rate(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    config = RATE_CONFIG.get(
        callback.data or ""
    )

    if not config:
        await _safe_answer(
            callback,
            "گزینه نامعتبر است.",
            show_alert=True,
        )
        return

    key, title = config

    await state.set_state(
        AdminActionStates.waiting_rate_value
    )

    await state.update_data(
        admin_rate_key=key,
        admin_rate_title=title,
    )

    await _safe_edit(
        callback,
        (
            f"✏️ <b>{escape(title)}</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "مقدار جدید را فقط به‌صورت عدد ارسال کنید.\n\n"
            "مثال: <code>95000</code>"
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_rate_value,
    F.text,
)
async def admin_rate_value_message(
    message: Message,
    state: FSMContext,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    data = await state.get_data()

    key = data.get(
        "admin_rate_key"
    )

    title = data.get(
        "admin_rate_title",
        key or "نرخ",
    )

    raw = (
        message.text
        or ""
    ).strip()

    cleaned = (
        raw
        .replace(",", "")
        .replace("٬", "")
        .replace("_", "")
        .replace(" ", "")
        .replace("٫", ".")
    )

    try:
        value = Decimal(
            cleaned
        )

    except (
        InvalidOperation,
        ValueError,
        TypeError,
    ):
        await message.answer(
            "❌ مقدار معتبر نیست. فقط عدد بزرگ‌تر از صفر بفرست."
        )
        return

    if (
        not value.is_finite()
        or value <= 0
    ):
        await message.answer(
            "❌ مقدار باید بزرگ‌تر از صفر باشد."
        )
        return

    if key not in {
        "usdt",
        "ton",
    }:
        await state.clear()
        await message.answer(
            "❌ نشست تنظیم نرخ معتبر نیست.",
            reply_markup=get_admin_main_menu(),
        )
        return

    success = finance_manager.update_rate(
        key,
        value,
        source="admin",
    )

    if not success:
        await message.answer(
            "❌ ذخیره نرخ انجام نشد."
        )
        return

    await state.clear()

    await message.answer(
        (
            "✅ <b>نرخ ذخیره شد.</b>\n\n"
            f"{escape(str(title))}: "
            f"<code>{escape(finance_manager.format_currency(value))}</code> تومان"
        ),
        reply_markup=get_admin_pricing_keyboard(),
        parse_mode="HTML",
    )


@router.callback_query(F.data == "auto_sync_rates")
async def admin_auto_sync_rates(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        result = await finance_manager.sync_rates()

        updated = set(
            result.get(
                "updated",
                (),
            )
            or ()
        )

        errors = dict(
            result.get(
                "errors",
                {},
            )
            or {}
        )

        if errors:
            status_lines = [
                "⚠️ <b>همگام‌سازی ناقص بود.</b>",
                "",
            ]

            status_lines.append(
                "✅ بروزرسانی‌شده: "
                + (
                    ", ".join(
                        sorted(updated)
                    )
                    if updated
                    else "هیچ‌کدام"
                )
            )

            status_lines.append(
                "⏸ نرخ ناموفق بدون تغییر قبلی حفظ شد: "
                + ", ".join(
                    sorted(
                        errors.keys()
                    )
                )
            )

            text = (
                _pricing_text()
                + "\n\n━━━━━━━━━━━━━━━━━━━━\n"
                + "\n".join(
                    status_lines
                )
            )

            answer_text = (
                "⚠️ بخشی از نرخ‌ها بروزرسانی شد؛ "
                "نرخ ناموفق قبلی حفظ شد."
            )

        else:
            text = (
                _pricing_text()
                + "\n\n✅ نرخ‌ها بروزرسانی شدند؛ USDT از Fallback صرافی‌های ایرانی و نرخ native از سرویس بازار."
            )

            answer_text = (
                "✅ نرخ‌های بازار بروزرسانی شدند."
            )

        logger.info(
            "Admin finance sync completed | result=%s",
            result,
        )

        await _safe_edit(
            callback,
            text,
            get_admin_pricing_keyboard(),
        )

        await _safe_answer(
            callback,
            answer_text,
            show_alert=bool(errors),
        )

    except Exception as exc:
        logger.exception(
            "Market rate synchronization failed."
        )

        await _safe_answer(
            callback,
            f"❌ بروزرسانی انجام نشد: {str(exc)[:120]}",
            show_alert=True,
        )


# ============================================================
# Product Base Prices (USDT)
# ============================================================

PRODUCT_ADMIN_LABELS: dict[
    str,
    str,
] = {
    "prem_1m":
        "Premium ۱ ماهه",

    "prem_3m":
        "Premium ۳ ماهه",

    "prem_6m":
        "Premium ۶ ماهه",

    "prem_1y":
        "Premium ۱ ساله",

    "stars_50":
        "50 Stars",

    "stars_100":
        "100 Stars",

    "stars_250":
        "250 Stars",

    "stars_500":
        "500 Stars",

    "stars_1000":
        "1000 Stars",

    "stars_5000":
        "5000 Stars",

    "stars_unit":
        "قیمت هر ۱ Star",
}


def _product_service(
    db: Optional[
        DatabaseManager
    ] = None,
) -> ProductPricingService:
    if db is not None:
        path = _db_path(
            db
        )

    else:
        path = str(
            getattr(
                settings,
                "DB_PATH",
                "matrix_bot.db",
            )
        )

    return ProductPricingService(
        path
    )


def _format_usdt(
    value: Optional[
        Decimal
    ],
) -> str:
    if value is None:
        return "تنظیم نشده"

    normalized = format(
        value.normalize(),
        "f",
    )

    return (
        normalized.rstrip("0").rstrip(".")
        if "." in normalized
        else normalized
    )


def _product_category_ids(
    category: str,
) -> tuple[
    str,
    ...,
]:
    if category == "premium":
        return PREMIUM_PRODUCT_IDS

    if category == "stars":
        return STARS_PRODUCT_IDS

    return ()


async def _product_prices_view(
    category: str,
    db: Optional[
        DatabaseManager
    ] = None,
) -> tuple[
    str,
    InlineKeyboardMarkup,
]:
    service = _product_service(
        db
    )

    prices = await service.get_all_base_usdt()

    ids = _product_category_ids(
        category
    )

    if category == "premium":
        title = "💎 قیمت پایه Premium"
        note = (
            "برای هر پکیج، قیمت کامل همان بسته را به USDT وارد کنید."
        )
        back_callback = "admin_pricing"

    else:
        title = "⭐ قیمت پایه Stars"
        note = (
            "فقط قیمت هر ۱ Star را وارد کنید. "
            "بسته‌های 50، 100، 250، 500، 1000، 5000 "
            "و Stars دلخواه همگی خودکار از روی همین مقدار حساب می‌شوند."
        )
        back_callback = "admin_pricing"

    lines = [
        f"<b>{title}</b>",
        "━━━━━━━━━━━━━━━━━━━━",
        "",
    ]

    builder = InlineKeyboardBuilder()

    for product_id in ids:
        label = PRODUCT_ADMIN_LABELS.get(
            product_id,
            PRODUCT_LABELS.get(
                product_id,
                product_id,
            ),
        )

        value = prices.get(
            product_id
        )

        value_text = _format_usdt(
            value
        )

        lines.append(
            f"• {escape(label)}: "
            f"<code>{escape(value_text)}"
            + (
                " USDT</code>"
                if value is not None
                else "</code>"
            )
        )

        builder.row(
            InlineKeyboardButton(
                text=(
                    f"{label} — "
                    + (
                        f"{value_text} USDT"
                        if value is not None
                        else "تنظیم نشده"
                    )
                ),
                callback_data=(
                    f"set_product_usdt_{product_id}"
                ),
                style=(
                    "primary"
                    if value is not None
                    else "danger"
                ),
            )
        )

    lines.extend(
        [
            "",
            note,
        ]
    )

    builder.row(
        InlineKeyboardButton(
            text="بازگشت به نرخ و قیمت‌ها",
            callback_data=back_callback,
            style="danger",
        )
    )

    return (
        "\n".join(
            lines
        ),
        builder.as_markup(),
    )


@router.callback_query(
    F.data == "admin_product_prices_premium"
)
async def admin_product_prices_premium(
    callback: CallbackQuery,
    state: FSMContext,
    db: Optional[
        DatabaseManager
    ] = None,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    text_value, keyboard = await _product_prices_view(
        "premium",
        db,
    )

    await _safe_edit(
        callback,
        text_value,
        keyboard,
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data == "admin_product_prices_stars"
)
async def admin_product_prices_stars(
    callback: CallbackQuery,
    state: FSMContext,
    db: Optional[
        DatabaseManager
    ] = None,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    text_value, keyboard = await _product_prices_view(
        "stars",
        db,
    )

    await _safe_edit(
        callback,
        text_value,
        keyboard,
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.startswith(
        "set_product_usdt_"
    )
)
async def admin_set_product_usdt(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    product_id = str(
        callback.data
        or ""
    ).removeprefix(
        "set_product_usdt_"
    )

    if product_id not in PRODUCT_LABELS:
        await _safe_answer(
            callback,
            "محصول نامعتبر است.",
            show_alert=True,
        )
        return

    label = PRODUCT_ADMIN_LABELS.get(
        product_id,
        PRODUCT_LABELS[
            product_id
        ],
    )

    await state.set_state(
        AdminActionStates.waiting_product_usdt_price
    )

    await state.update_data(
        admin_product_id=product_id,
        admin_product_label=label,
        admin_product_category=(
            "premium"
            if product_id.startswith(
                "prem_"
            )
            else "stars"
        ),
    )

    hint = (
        "مثال: <code>12</code>"
        if product_id.startswith(
            "prem_"
        )
        else (
            "مثال: <code>0.02</code> یعنی 50 Stars = 1 USDT"
            if product_id
            == "stars_unit"
            else "مثال: <code>2.5</code>"
        )
    )

    await _safe_edit(
        callback,
        (
            f"✏️ <b>{escape(label)}</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "قیمت پایه را به <b>USDT</b> ارسال کنید.\n"
            "فقط عدد مثبت وارد کنید.\n\n"
            f"{hint}\n\n"
            "برای Stars، تمام بسته‌ها از همین قیمت واحد محاسبه می‌شوند.\n"
            "بعداً قیمت تومان با نرخ زنده تتر محاسبه می‌شود."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_product_usdt_price,
    F.text,
)
async def admin_product_usdt_value(
    message: Message,
    state: FSMContext,
    db: Optional[
        DatabaseManager
    ] = None,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    data = await state.get_data()

    product_id = str(
        data.get(
            "admin_product_id",
            "",
        )
        or ""
    )

    label = str(
        data.get(
            "admin_product_label",
            product_id,
        )
    )

    category = str(
        data.get(
            "admin_product_category",
            "stars",
        )
    )

    if product_id not in PRODUCT_LABELS:
        await state.clear()

        await message.answer(
            "❌ نشست تنظیم قیمت معتبر نیست.",
            reply_markup=get_admin_main_menu(),
        )
        return

    raw = (
        message.text
        or ""
    ).strip()

    cleaned = (
        raw
        .replace("٬", "")
        .replace("_", "")
        .replace(" ", "")
        .replace("٫", ".")
        .replace(",", ".")
    )

    try:
        value = Decimal(
            cleaned
        )

    except (
        InvalidOperation,
        TypeError,
        ValueError,
    ):
        await message.answer(
            "❌ قیمت معتبر نیست. مثال: 12 یا 0.02"
        )
        return

    if (
        not value.is_finite()
        or value <= 0
    ):
        await message.answer(
            "❌ قیمت باید بزرگ‌تر از صفر باشد."
        )
        return

    service = _product_service(
        db
    )

    try:
        stored = await service.set_base_usdt(
            product_id,
            value,
            updated_by=(
                message.from_user.id
                if message.from_user
                else None
            ),
        )

    except InvalidProductPrice:
        await message.answer(
            "❌ قیمت واردشده معتبر نیست."
        )
        return

    await state.clear()

    text_value, keyboard = await _product_prices_view(
        category,
        db,
    )

    await message.answer(
        (
            "✅ <b>قیمت محصول ذخیره شد.</b>\n\n"
            f"{escape(label)}: "
            f"<code>{escape(_format_usdt(stored))} USDT</code>\n\n"
            + text_value
        ),
        reply_markup=keyboard,
        parse_mode="HTML",
    )


# ============================================================
# Users
# ============================================================

async def _user_details_text(
    db: DatabaseManager,
    user_id: int,
) -> tuple[str, Optional[dict[str, Any]]]:
    user = await db.get_user(
        user_id
    )

    if not user:
        return (
            "❌ کاربر پیدا نشد.",
            None,
        )

    username = (
        f"@{user.get('username')}"
        if user.get(
            "username"
        )
        else "ندارد"
    )

    text = (
        "👤 <b>پروفایل مدیریتی کاربر</b>\n"
        "━━━━━━━━━━━━━━━━━━━━\n\n"

        f"ID: <code>{user_id}</code>\n"
        f"Username: <code>{escape(username)}</code>\n"
        f"نام: <b>{escape(str(user.get('full_name') or '—'))}</b>\n\n"

        f"💰 تومان: <code>{_fmt_int(user.get('balance_toman'))}</code>\n"
        f"🔷 TON: <code>{escape(str(user.get('balance_ton') or 0))}</code>\n"
        f"💵 USDT: <code>{escape(str(user.get('balance_usdt') or 0))}</code>\n\n"

        f"🚫 مسدود: <b>{'بله' if user.get('is_banned') else 'خیر'}</b>\n"
        f"🛡 KYC: <b>{escape(str(user.get('kyc_status') or 'none'))}</b>\n"
        f"🎖 سطح: <b>{escape(str(user.get('user_tier') or 'standard'))}</b>"
    )

    return (
        text,
        user,
    )


@router.callback_query(
    F.data.in_(
        {
            "admin_search_user",
            "admin_users_list",
        }
    )
)
async def admin_users_entry(
    callback: CallbackQuery,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    if callback.data == "admin_users_list":
        users = await db.get_all_users(
            limit=20
        )

        if users:
            lines = []

            for user in users:
                uid = int(
                    user.get(
                        "user_id",
                        0,
                    )
                    or 0
                )

                username = (
                    f"@{user.get('username')}"
                    if user.get(
                        "username"
                    )
                    else "—"
                )

                lines.append(
                    f"• <code>{uid}</code> | "
                    f"{escape(username)} | "
                    f"{'🚫' if user.get('is_banned') else '✅'}"
                )

            text = (
                "👥 <b>آخرین کاربران</b>\n"
                "━━━━━━━━━━━━━━━━━━━━\n\n"
                + "\n".join(
                    lines
                )
                + "\n\nبرای مدیریت دقیق، دکمه جستجو را بزنید یا "
                "<code>/admin</code> را باز کنید."
            )

        else:
            text = "هیچ کاربری ثبت نشده است."

        await _safe_edit(
            callback,
            text,
            _back_admin_keyboard(),
        )

        await _safe_answer(
            callback
        )
        return

    await state.set_state(
        AdminActionStates.waiting_user_search
    )

    await _safe_edit(
        callback,
        (
            "🔎 <b>جستجوی کاربر</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "Telegram User ID یا Username را ارسال کنید.\n\n"
            "مثال:\n"
            "<code>123456789</code>\n"
            "<code>@username</code>"
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_user_search,
    F.text,
)
async def admin_user_search_message(
    message: Message,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    raw = (
        message.text
        or ""
    ).strip()

    user: Optional[
        dict[str, Any]
    ] = None

    if raw.isdigit():
        user = await db.get_user(
            int(raw)
        )

    else:
        username = (
            raw.lstrip("@")
            .strip()
            .lower()
        )

        users = await db.get_all_users(
            limit=1000
        )

        for item in users:
            current = str(
                item.get(
                    "username",
                    "",
                )
                or ""
            ).lower()

            if current == username:
                user = item
                break

    if not user:
        await message.answer(
            "❌ کاربر پیدا نشد. دوباره ID یا Username را بفرست."
        )
        return

    await state.clear()

    user_id = int(
        user[
            "user_id"
        ]
    )

    text, _ = await _user_details_text(
        db,
        user_id,
    )

    await message.answer(
        text,
        reply_markup=get_admin_user_manage_keyboard(
            user_id=user_id,
            is_blocked=bool(
                user.get(
                    "is_banned"
                )
            ),
            is_admin=bool(
                user.get(
                    "is_admin"
                )
            ),
        ),
        parse_mode="HTML",
    )


@router.callback_query(
    F.data.startswith(
        "admin_user_view_"
    )
)
async def admin_user_view(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        await _safe_answer(
            callback,
            "ID نامعتبر است.",
            show_alert=True,
        )
        return

    text, user = await _user_details_text(
        db,
        user_id,
    )

    keyboard = (
        get_admin_user_manage_keyboard(
            user_id=user_id,
            is_blocked=bool(
                user.get(
                    "is_banned"
                )
            ),
            is_admin=bool(
                user.get(
                    "is_admin"
                )
            ),
        )
        if user
        else _back_admin_keyboard()
    )

    await _safe_edit(
        callback,
        text,
        keyboard,
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.startswith("admin_block_")
)
async def admin_block_user(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        await _safe_answer(
            callback,
            "ID نامعتبر است.",
            show_alert=True,
        )
        return

    if _is_admin(
        user_id
    ):
        await _safe_answer(
            callback,
            "ادمین تنظیم‌شده در Config را از این بخش نمی‌توان مسدود کرد.",
            show_alert=True,
        )
        return

    success = await db.set_user_ban_status(
        user_id,
        True,
    )

    await _safe_answer(
        callback,
        (
            "✅ کاربر مسدود شد."
            if success
            else "❌ عملیات ناموفق بود."
        ),
        show_alert=not success,
    )

    if success:
        text, user = await _user_details_text(
            db,
            user_id,
        )

        await _safe_edit(
            callback,
            text,
            get_admin_user_manage_keyboard(
                user_id=user_id,
                is_blocked=True,
                is_admin=bool(
                    (user or {}).get(
                        "is_admin"
                    )
                ),
            ),
        )


@router.callback_query(
    F.data.startswith("admin_unblock_")
)
async def admin_unblock_user(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        await _safe_answer(
            callback,
            "ID نامعتبر است.",
            show_alert=True,
        )
        return

    success = await db.set_user_ban_status(
        user_id,
        False,
    )

    await _safe_answer(
        callback,
        (
            "✅ مسدودی برداشته شد."
            if success
            else "❌ عملیات ناموفق بود."
        ),
        show_alert=not success,
    )

    if success:
        text, user = await _user_details_text(
            db,
            user_id,
        )

        await _safe_edit(
            callback,
            text,
            get_admin_user_manage_keyboard(
                user_id=user_id,
                is_blocked=False,
                is_admin=bool(
                    (user or {}).get(
                        "is_admin"
                    )
                ),
            ),
        )


@router.callback_query(
    F.data.startswith("admin_promote_")
    | F.data.startswith("admin_demote_")
)
async def admin_role_config_notice(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await _safe_answer(
        callback,
        (
            "ℹ️ دسترسی مدیر از ADMIN_IDS در Config مدیریت می‌شود؛ "
            "برای امنیت از دیتابیس به‌صورت پویا تغییر داده نمی‌شود."
        ),
        show_alert=True,
    )


# ============================================================
# Wallet management
# ============================================================

@router.callback_query(F.data == "admin_wallet_adjust")
async def admin_wallet_adjust(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.set_state(
        AdminActionStates.waiting_wallet_user
    )

    await _safe_edit(
        callback,
        (
            "💰 <b>تغییر دستی موجودی</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "Telegram User ID کاربر را ارسال کنید."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_wallet_user,
    F.text,
)
async def admin_wallet_user_message(
    message: Message,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    raw = (
        message.text
        or ""
    ).strip()

    if not raw.isdigit():
        await message.answer(
            "❌ فقط User ID عددی ارسال کنید."
        )
        return

    user_id = int(
        raw
    )

    user = await db.get_user(
        user_id
    )

    if not user:
        await message.answer(
            "❌ کاربر پیدا نشد."
        )
        return

    await state.clear()

    await message.answer(
        (
            "💰 <b>مدیریت موجودی</b>\n\n"
            f"کاربر: <code>{user_id}</code>\n"
            f"موجودی: <code>{_fmt_int(user.get('balance_toman'))}</code> تومان"
        ),
        reply_markup=get_admin_wallet_adjust_keyboard(
            user_id
        ),
        parse_mode="HTML",
    )


@router.callback_query(
    F.data.startswith("admin_edit_wallet_")
)
async def admin_edit_wallet(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        await _safe_answer(
            callback,
            "ID نامعتبر است.",
            show_alert=True,
        )
        return

    user = await db.get_user(
        user_id
    )

    if not user:
        await _safe_answer(
            callback,
            "کاربر پیدا نشد.",
            show_alert=True,
        )
        return

    await _safe_edit(
        callback,
        (
            "💰 <b>مدیریت موجودی</b>\n\n"
            f"کاربر: <code>{user_id}</code>\n"
            f"موجودی: <code>{_fmt_int(user.get('balance_toman'))}</code> تومان"
        ),
        get_admin_wallet_adjust_keyboard(
            user_id
        ),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.startswith("admin_wallet_view_")
)
async def admin_wallet_view(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    balance = await db.get_balance(
        user_id
    )

    await _safe_answer(
        callback,
        f"موجودی: {balance:,} تومان",
        show_alert=True,
    )


@router.callback_query(
    F.data.startswith("admin_wallet_add_")
    | F.data.startswith("admin_wallet_remove_")
)
async def admin_wallet_amount_prompt(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    data = (
        callback.data
        or ""
    )

    mode = (
        "add"
        if data.startswith(
            "admin_wallet_add_"
        )
        else "remove"
    )

    try:
        user_id = int(
            data.rsplit(
                "_",
                1,
            )[-1]
        )

    except ValueError:
        await _safe_answer(
            callback,
            "ID نامعتبر است.",
            show_alert=True,
        )
        return

    await state.set_state(
        AdminActionStates.waiting_wallet_amount
    )

    await state.update_data(
        wallet_user_id=user_id,
        wallet_mode=mode,
    )

    action = (
        "افزایش"
        if mode == "add"
        else "کاهش"
    )

    await _safe_edit(
        callback,
        (
            f"💰 <b>{action} موجودی</b>\n\n"
            f"کاربر: <code>{user_id}</code>\n\n"
            "مبلغ را به تومان ارسال کنید."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_wallet_amount,
    F.text,
)
async def admin_wallet_amount_message(
    message: Message,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    amount = _parse_positive_int(
        message.text or ""
    )

    if amount is None:
        await message.answer(
            "❌ مبلغ معتبر نیست."
        )
        return

    data = await state.get_data()

    user_id = int(
        data.get(
            "wallet_user_id",
            0,
        )
        or 0
    )

    mode = data.get(
        "wallet_mode"
    )

    if (
        user_id <= 0
        or mode not in {
            "add",
            "remove",
        }
    ):
        await state.clear()
        await message.answer(
            "❌ نشست مدیریت موجودی نامعتبر است."
        )
        return

    admin_id = (
        message.from_user.id
        if message.from_user
        else 0
    )

    reference = (
        f"admin:{admin_id}:{user_id}:"
        f"{mode}:{time.time_ns()}"
    )

    if mode == "add":
        success = await db.increase_user_balance(
            user_id,
            amount,
            description=(
                f"Admin credit by {admin_id}"
            ),
            reference_id=reference,
        )

    else:
        success = await db.decrease_user_balance(
            user_id,
            amount,
            description=(
                f"Admin debit by {admin_id}"
            ),
            reference_id=reference,
        )

    if not success:
        await message.answer(
            (
                "❌ عملیات انجام نشد. "
                "برای کاهش موجودی، موجودی کاربر را هم بررسی کن."
            )
        )
        return

    await state.clear()

    balance = await db.get_balance(
        user_id
    )

    await message.answer(
        (
            "✅ <b>موجودی بروزرسانی شد.</b>\n\n"
            f"کاربر: <code>{user_id}</code>\n"
            f"موجودی جدید: <code>{balance:,}</code> تومان"
        ),
        reply_markup=get_admin_wallet_adjust_keyboard(
            user_id
        ),
        parse_mode="HTML",
    )


@router.callback_query(
    F.data.startswith("admin_user_history_")
    | F.data.startswith("admin_wallet_history_")
)
async def admin_user_history(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )

    except ValueError:
        return

    txs = await db.get_wallet_transactions(
        user_id,
        limit=15,
    )

    if not txs:
        text = (
            "📭 تراکنشی برای این کاربر ثبت نشده است."
        )

    else:
        lines = []

        for tx in txs:
            lines.append(
                (
                    f"• #{tx.get('id')} | "
                    f"<b>{escape(str(tx.get('type') or '—'))}</b> | "
                    f"<code>{_fmt_int(tx.get('amount'))}</code> تومان\n"
                    f"  موجودی: "
                    f"<code>{_fmt_int(tx.get('balance_before'))}</code> → "
                    f"<code>{_fmt_int(tx.get('balance_after'))}</code>"
                )
            )

        text = (
            f"📜 <b>تراکنش‌های کاربر {user_id}</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            + "\n\n".join(
                lines
            )
        )

    await _safe_edit(
        callback,
        text,
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


# ============================================================
# Direct message
# ============================================================

@router.callback_query(
    F.data.startswith("admin_send_msg_")
)
async def admin_direct_message_prompt(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    await state.set_state(
        AdminActionStates.waiting_direct_message
    )

    await state.update_data(
        direct_user_id=user_id
    )

    await _safe_edit(
        callback,
        (
            "✉️ <b>ارسال پیام مستقیم</b>\n\n"
            f"کاربر: <code>{user_id}</code>\n\n"
            "متن پیام را ارسال کنید."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_direct_message,
    F.text,
)
async def admin_direct_message_send(
    message: Message,
    state: FSMContext,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    data = await state.get_data()

    user_id = int(
        data.get(
            "direct_user_id",
            0,
        )
        or 0
    )

    if user_id <= 0:
        await state.clear()
        return

    try:
        await message.bot.send_message(
            chat_id=user_id,
            text=(
                message.text
                or ""
            ),
        )

        await state.clear()

        await message.answer(
            "✅ پیام ارسال شد.",
            reply_markup=get_admin_main_menu(),
        )

    except TelegramForbiddenError:
        await message.answer(
            "❌ کاربر ربات را مسدود کرده است."
        )

    except Exception:
        logger.exception(
            "Direct admin message failed | user=%s",
            user_id,
        )

        await message.answer(
            "❌ ارسال پیام ناموفق بود."
        )


# ============================================================
# Orders
# ============================================================

@router.callback_query(F.data == "admin_orders_manage")
async def admin_orders_manage(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    await _safe_edit(
        callback,
        (
            "📦 <b>مدیریت سفارش‌ها</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "فیلتر موردنظر را انتخاب کنید."
        ),
        get_admin_orders_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.in_(
        {
            "admin_orders_all",
            "admin_orders_new",
            "admin_orders_processing",
            "admin_orders_completed",
            "admin_orders_cancelled",
        }
    )
)
async def admin_orders_list(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    orders = await db.get_all_orders(
        limit=60
    )

    data = callback.data

    if data == "admin_orders_new":
        accepted = {
            "pending_owner_approval",
            "pending",
            "created",
        }

    elif data == "admin_orders_processing":
        accepted = {
            "owner_processing",
            "provider_processing",
            "processing",
            "provider_status_unknown",
        }

    elif data == "admin_orders_completed":
        accepted = {
            "completed",
        }

    elif data == "admin_orders_cancelled":
        accepted = {
            "rejected_by_owner",
            "cancelled",
            "failed_refunded",
            "refunded",
            "failed",
        }

    else:
        accepted = None

    if accepted is not None:
        orders = [
            item
            for item
            in orders
            if str(
                item.get(
                    "status",
                    "",
                )
            ) in accepted
        ]

    orders = orders[:20]

    if not orders:
        text = "📭 سفارشی در این بخش وجود ندارد."

    else:
        lines = []

        for order in orders:
            status = str(
                order.get(
                    "status",
                    "",
                )
                or "—"
            )

            created_at = format_order_datetime(
                order.get(
                    "created_at"
                )
            )

            if status == "completed":
                final_label = "✅ انجام"
                final_time = order_event_time(
                    order,
                    "completed_at",
                    "updated_at",
                )

            elif status in {
                "failed_refunded",
                "refunded",
            }:
                final_label = "↩️ بازگشت وجه"
                final_time = order_event_time(
                    order,
                    "refunded_at",
                    "updated_at",
                )

            elif status in {
                "rejected_by_owner",
                "cancelled",
                "cancelled_by_user",
                "failed",
            }:
                final_label = "❌ پایان"
                final_time = order_event_time(
                    order,
                    "updated_at",
                )

            elif status in {
                "provider_processing",
                "owner_processing",
                "provider_status_unknown",
            }:
                final_label = "⚙️ پردازش"
                final_time = order_event_time(
                    order,
                    "provider_started_at",
                    "updated_at",
                )

            else:
                final_label = "🕒 آخرین تغییر"
                final_time = order_event_time(
                    order,
                    "updated_at",
                )

            lines.append(
                (
                    f"🧾 <b>سفارش #{order.get('id')}</b>\n"
                    f"👤 کاربر: <code>{order.get('user_id')}</code>\n"
                    f"📦 محصول: <code>{escape(str(order.get('product_id') or '—'))}</code>\n"
                    f"💰 مبلغ: <code>{_fmt_int(order.get('price'))} تومان</code>\n"
                    f"📌 وضعیت: <code>{escape(status)}</code>\n"
                    f"🕒 ثبت: <code>{created_at}</code>\n"
                    f"{final_label}: <code>{final_time}</code>\n"
                    "──────────────"
                )
            )

        text = (
            "📦 <b>آخرین سفارش‌ها</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            + "\n\n".join(
                lines
            )
        )

    await _safe_edit(
        callback,
        text,
        get_admin_orders_keyboard(),
    )

    await _safe_answer(
        callback
    )


# ============================================================
# Broadcast
# ============================================================

@router.callback_query(F.data == "admin_broadcast")
async def admin_broadcast(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    await _safe_edit(
        callback,
        (
            "📢 <b>ارسال پیام همگانی</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            "گروه هدف را انتخاب کنید."
        ),
        get_admin_broadcast_options_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.in_(
        {
            "broadcast_all",
            "broadcast_kyc_passed",
            "broadcast_positive_balance",
        }
    )
)
async def admin_broadcast_target(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.set_state(
        AdminActionStates.waiting_broadcast_text
    )

    await state.update_data(
        broadcast_target=callback.data,
    )

    await _safe_edit(
        callback,
        (
            "📢 <b>متن پیام همگانی را ارسال کنید.</b>\n\n"
            "پیام بعدی شما برای گروه انتخاب‌شده ارسال می‌شود."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


def _broadcast_user_ids(
    db_path: str,
    target: str,
) -> list[int]:
    where = ""

    if target == "broadcast_kyc_passed":
        where = (
            "WHERE COALESCE(is_kyc_verified, 0) = 1"
        )

    elif target == "broadcast_positive_balance":
        where = (
            "WHERE COALESCE(balance_toman, 0) > 0"
        )

    rows = _sqlite_rows(
        db_path,
        f"""
        SELECT user_id
        FROM users
        {where}
        ORDER BY user_id
        """,
    )

    return [
        int(
            row[
                "user_id"
            ]
        )
        for row
        in rows
    ]


@router.message(
    AdminActionStates.waiting_broadcast_text,
    F.text,
)
async def admin_broadcast_send(
    message: Message,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    data = await state.get_data()

    target = str(
        data.get(
            "broadcast_target",
            "",
        )
    )

    if target not in {
        "broadcast_all",
        "broadcast_kyc_passed",
        "broadcast_positive_balance",
    }:
        await state.clear()
        return

    user_ids = await asyncio.to_thread(
        _broadcast_user_ids,
        _db_path(db),
        target,
    )

    await state.clear()

    status = await message.answer(
        (
            "⏳ ارسال شروع شد...\n"
            f"تعداد مقصد: <code>{len(user_ids):,}</code>"
        ),
        parse_mode="HTML",
    )

    sent = 0
    failed = 0

    for user_id in user_ids:
        try:
            await message.bot.send_message(
                chat_id=user_id,
                text=(
                    message.text
                    or ""
                ),
            )

            sent += 1

        except (
            TelegramForbiddenError,
            TelegramBadRequest,
        ):
            failed += 1

        except Exception:
            failed += 1

            logger.exception(
                "Broadcast send failed | user=%s",
                user_id,
            )

        # Gentle pacing.
        await asyncio.sleep(
            0.035
        )

    try:
        await status.edit_text(
            (
                "✅ <b>ارسال همگانی پایان یافت.</b>\n\n"
                f"موفق: <code>{sent:,}</code>\n"
                f"ناموفق: <code>{failed:,}</code>"
            ),
            reply_markup=get_admin_main_menu(),
            parse_mode="HTML",
        )

    except Exception:
        pass


# ============================================================
# KYC
# ============================================================

def _pending_kyc(
    db_path: str,
) -> list[dict[str, Any]]:
    return _sqlite_rows(
        db_path,
        """
        SELECT
            k.id,
            k.user_id,
            k.file_id,
            k.status,
            k.created_at,
            u.username,
            u.full_name
        FROM kyc_requests AS k
        LEFT JOIN users AS u
            ON u.user_id = k.user_id
        WHERE k.status = 'pending'
        ORDER BY k.id DESC
        LIMIT 20
        """,
    )


@router.callback_query(F.data == "admin_kyc_queue")
async def admin_kyc_queue(
    callback: CallbackQuery,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await state.clear()

    rows = await asyncio.to_thread(
        _pending_kyc,
        _db_path(db),
    )

    builder = InlineKeyboardBuilder()

    if rows:
        for item in rows:
            request_id = int(
                item[
                    "id"
                ]
            )

            user_id = int(
                item[
                    "user_id"
                ]
            )

            username = (
                f"@{item.get('username')}"
                if item.get(
                    "username"
                )
                else str(
                    user_id
                )
            )

            builder.row(
                InlineKeyboardButton(
                    text=(
                        f"درخواست #{request_id} | "
                        f"{username}"
                    ),
                    callback_data=(
                        f"admin_kyc_view_{request_id}"
                    ),
                    style="primary",
                )
            )

    builder.row(
        InlineKeyboardButton(
            text="بازگشت به پنل مدیریت",
            callback_data="admin_main",
            style="danger",
        )
    )

    text = (
        "🛡 <b>صف احراز هویت</b>\n"
        "━━━━━━━━━━━━━━━━━━━━\n\n"
        f"در انتظار بررسی: <code>{len(rows)}</code>\n\n"
        + (
            "یک درخواست را انتخاب کنید."
            if rows
            else "درخواستی در انتظار بررسی نیست."
        )
    )

    await _safe_edit(
        callback,
        text,
        builder.as_markup(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.startswith("admin_kyc_view_")
)
async def admin_kyc_view(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        request_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    row = await asyncio.to_thread(
        _sqlite_one,
        _db_path(db),
        """
        SELECT
            k.*,
            u.username,
            u.full_name
        FROM kyc_requests AS k
        LEFT JOIN users AS u
            ON u.user_id = k.user_id
        WHERE k.id = ?
        LIMIT 1
        """,
        (
            request_id,
        ),
    )

    if not row:
        await _safe_answer(
            callback,
            "درخواست پیدا نشد.",
            show_alert=True,
        )
        return

    user_id = int(
        row[
            "user_id"
        ]
    )

    builder = InlineKeyboardBuilder()

    builder.row(
        InlineKeyboardButton(
            text="تایید احراز هویت",
            callback_data=(
                f"admin_kyc_approve_{request_id}"
            ),
            style="success",
        ),
        InlineKeyboardButton(
            text="رد احراز هویت",
            callback_data=(
                f"admin_kyc_reject_{request_id}"
            ),
            style="danger",
        ),
    )

    builder.row(
        InlineKeyboardButton(
            text="بازگشت به صف",
            callback_data="admin_kyc_queue",
            style="primary",
        )
    )

    await _safe_edit(
        callback,
        (
            "🛡 <b>جزئیات KYC</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            f"Request ID: <code>{request_id}</code>\n"
            f"User ID: <code>{user_id}</code>\n"
            f"Username: "
            f"<code>{escape(str(row.get('username') or '—'))}</code>\n"
            f"نام: <b>{escape(str(row.get('full_name') or '—'))}</b>\n"
            f"وضعیت: <code>{escape(str(row.get('status') or '—'))}</code>\n"
            f"زمان: <code>{escape(str(row.get('created_at') or '—'))}</code>\n\n"
            "File ID:\n"
            f"<code>{escape(str(row.get('file_id') or '—'))}</code>"
        ),
        builder.as_markup(),
    )

    await _safe_answer(
        callback
    )


async def _kyc_set_status(
    db: DatabaseManager,
    user_id: int,
    status: str,
    *,
    admin_id: int,
    reason: Optional[str] = None,
) -> bool:
    return await db.set_user_kyc_status(
        user_id,
        status,
        reviewed_by=admin_id,
        rejection_reason=reason,
    )


@router.callback_query(
    F.data.startswith("admin_kyc_approve_")
)
async def admin_kyc_approve(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        request_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    row = await asyncio.to_thread(
        _sqlite_one,
        _db_path(db),
        """
        SELECT user_id
        FROM kyc_requests
        WHERE id = ?
        LIMIT 1
        """,
        (
            request_id,
        ),
    )

    if not row:
        await _safe_answer(
            callback,
            "درخواست پیدا نشد.",
            show_alert=True,
        )
        return

    user_id = int(
        row[
            "user_id"
        ]
    )

    admin_id = callback.from_user.id

    success = await _kyc_set_status(
        db,
        user_id,
        "approved",
        admin_id=admin_id,
    )

    if success:
        try:
            await callback.bot.send_message(
                user_id,
                "✅ احراز هویت شما تایید شد.",
            )
        except Exception:
            pass

    await _safe_answer(
        callback,
        (
            "✅ احراز هویت تایید شد."
            if success
            else "❌ ثبت تایید ناموفق بود."
        ),
        show_alert=not success,
    )

    if success:
        await _show_kyc_done(
            callback,
            "✅ <b>احراز هویت تایید شد.</b>",
        )


# Lightweight post-action page.
async def _show_kyc_done(
    callback: CallbackQuery,
    text: str,
) -> None:
    await _safe_edit(
        callback,
        text,
        _back_and_refresh_keyboard(
            "admin_kyc_queue"
        ),
    )


# Re-register with a more specific callback handler name is not possible;
# instead the approve handler returns before the invalid context call using
# this monkey-safe wrapper below. The source is patched after definition.


@router.callback_query(
    F.data.startswith("admin_kyc_reject_")
)
async def admin_kyc_reject(
    callback: CallbackQuery,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        request_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    row = await asyncio.to_thread(
        _sqlite_one,
        _db_path(db),
        """
        SELECT user_id
        FROM kyc_requests
        WHERE id = ?
        LIMIT 1
        """,
        (
            request_id,
        ),
    )

    if not row:
        await _safe_answer(
            callback,
            "درخواست پیدا نشد.",
            show_alert=True,
        )
        return

    await state.set_state(
        AdminActionStates.waiting_kyc_reject_reason
    )

    await state.update_data(
        kyc_reject_user_id=int(
            row[
                "user_id"
            ]
        )
    )

    await _safe_edit(
        callback,
        (
            "❌ <b>رد احراز هویت</b>\n\n"
            "دلیل رد را ارسال کنید."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.message(
    AdminActionStates.waiting_kyc_reject_reason,
    F.text,
)
async def admin_kyc_reject_reason(
    message: Message,
    state: FSMContext,
    db: DatabaseManager,
) -> None:
    if not await _guard_message(
        message
    ):
        await state.clear()
        return

    data = await state.get_data()

    user_id = int(
        data.get(
            "kyc_reject_user_id",
            0,
        )
        or 0
    )

    reason = (
        message.text
        or ""
    ).strip()

    if (
        user_id <= 0
        or len(reason) < 3
    ):
        await message.answer(
            "❌ دلیل رد باید حداقل ۳ کاراکتر باشد."
        )
        return

    admin_id = message.from_user.id

    success = await _kyc_set_status(
        db,
        user_id,
        "rejected",
        admin_id=admin_id,
        reason=reason[:1000],
    )

    if not success:
        await message.answer(
            "❌ ثبت رد احراز هویت ناموفق بود."
        )
        return

    await state.clear()

    try:
        await message.bot.send_message(
            user_id,
            (
                "❌ احراز هویت شما رد شد.\n\n"
                f"دلیل: {reason[:1000]}"
            ),
        )
    except Exception:
        pass

    await message.answer(
        "✅ رد احراز هویت ثبت شد.",
        reply_markup=get_admin_main_menu(),
    )


# Legacy KYC buttons emitted by older user KYC handler.
@router.callback_query(
    F.data.startswith("kyc_approve_")
)
async def admin_kyc_legacy_approve(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    success = await _kyc_set_status(
        db,
        user_id,
        "approved",
        admin_id=callback.from_user.id,
    )

    await _safe_answer(
        callback,
        (
            "✅ تایید شد."
            if success
            else "❌ ناموفق بود."
        ),
        show_alert=not success,
    )


@router.callback_query(
    F.data.startswith("kyc_reject_")
)
async def admin_kyc_legacy_reject(
    callback: CallbackQuery,
    state: FSMContext,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    try:
        user_id = int(
            (callback.data or "")
            .rsplit("_", 1)[-1]
        )
    except ValueError:
        return

    await state.set_state(
        AdminActionStates.waiting_kyc_reject_reason
    )

    await state.update_data(
        kyc_reject_user_id=user_id
    )

    await _safe_edit(
        callback,
        "دلیل رد احراز هویت را ارسال کنید.",
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


# ============================================================
# Server status
# ============================================================

def _human_uptime() -> str:
    seconds = int(
        time.monotonic()
        - STARTED_AT
    )

    hours, remainder = divmod(
        seconds,
        3600,
    )

    minutes, seconds = divmod(
        remainder,
        60,
    )

    return (
        f"{hours:02d}:{minutes:02d}:{seconds:02d}"
    )


@router.callback_query(F.data == "admin_server_status")
async def admin_server_status(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    db_ok = await db.health_check()

    product_prices = await _product_service(
        db
    ).get_all_base_usdt()

    configured_products = sum(
        1
        for value
        in product_prices.values()
        if value is not None
    )

    await _safe_edit(
        callback,
        (
            "🖥 <b>وضعیت سرور و سرویس‌ها</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"

            f"🐍 Python: <code>{escape(platform.python_version())}</code>\n"
            f"⏱ Uptime: <code>{_human_uptime()}</code>\n"
            f"🗄 Database: <b>{'✅ سالم' if db_ok else '❌ خطا'}</b>\n"
            f"💵 قیمت‌های پایه: <b>{configured_products}/{len(PRODUCT_LABELS)} تنظیم‌شده</b>\n"
            "📈 USDT/Toman: <b>Fallback چند صرافی عمومی</b>\n"
            "📦 تحویل سفارش: <b>دستی توسط مالک</b>"
        ),
        get_admin_server_status_keyboard(),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(
    F.data.in_(
        {
            "admin_check_database",
            "admin_check_apis",
            "admin_check_wallets",
        }
    )
)
async def admin_server_checks(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    if callback.data == "admin_check_database":
        ok = await db.health_check()

        text = (
            "✅ دیتابیس سالم است."
            if ok
            else "❌ Health check دیتابیس ناموفق بود."
        )

    elif callback.data == "admin_check_wallets":
        stats = await db.get_statistics()

        text = (
            "💳 مجموع موجودی ثبت‌شده کاربران:\n"
            f"{_fmt_int(stats.get('wallet_balance_toman'))} تومان"
        )

    else:
        prices = await _product_service(
            db
        ).get_all_base_usdt()

        configured_count = sum(
            1
            for value
            in prices.values()
            if value is not None
        )

        text = (
            "💵 Product pricing: "
            f"{configured_count}/{len(PRODUCT_LABELS)} configured"
            "\nUSDT/Toman market data: public multi-exchange fallback"
            "\nDelivery: manual owner fulfillment"
        )

    await _safe_answer(
        callback,
        text,
        show_alert=True,
    )


# ============================================================
# Logs
# ============================================================

@router.callback_query(F.data == "admin_logs_view")
async def admin_logs_menu(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    keyboard = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(
                    text="لاگ‌های اخیر",
                    callback_data="admin_logs_recent",
                    style="primary",
                ),
                InlineKeyboardButton(
                    text="خطاهای اخیر",
                    callback_data="admin_logs_errors",
                    style="danger",
                ),
            ],
            [
                InlineKeyboardButton(
                    text="بازگشت",
                    callback_data="admin_main",
                    style="danger",
                )
            ],
        ]
    )

    await _safe_edit(
        callback,
        (
            "📝 <b>مدیریت لاگ‌ها</b>\n\n"
            "نوع لاگ را انتخاب کنید."
        ),
        keyboard,
    )

    await _safe_answer(
        callback
    )


def _read_log_lines(
    *,
    errors_only: bool,
    limit: int = 35,
) -> str:
    path = Path(
        "matrix_bot.log"
    )

    if not path.is_file():
        return "فایل matrix_bot.log پیدا نشد."

    try:
        lines = path.read_text(
            encoding="utf-8",
            errors="replace",
        ).splitlines()

    except Exception as exc:
        return (
            f"خطا در خواندن لاگ: {exc}"
        )

    if errors_only:
        lines = [
            line
            for line
            in lines
            if (
                " - ERROR - " in line
                or " - CRITICAL - " in line
            )
        ]

    selected = lines[
        -limit:
    ]

    content = "\n".join(
        selected
    )

    # Keep Telegram HTML message below limit.
    return content[
        -3200:
    ]


@router.callback_query(
    F.data.in_(
        {
            "admin_logs_recent",
            "admin_logs_errors",
            "admin_logs_security",
        }
    )
)
async def admin_logs_show(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    errors_only = (
        callback.data
        == "admin_logs_errors"
    )

    content = await asyncio.to_thread(
        _read_log_lines,
        errors_only=errors_only,
    )

    await _safe_edit(
        callback,
        (
            "📝 <b>لاگ سیستم</b>\n\n"
            f"<pre>{escape(content)}</pre>"
        ),
        _back_and_refresh_keyboard(
            callback.data
            or "admin_logs_view"
        ),
    )

    await _safe_answer(
        callback
    )


@router.callback_query(F.data == "admin_logs_clear")
async def admin_logs_clear_disabled(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    await _safe_answer(
        callback,
        (
            "برای جلوگیری از حذف تصادفی شواهد خطا، "
            "پاک‌سازی لاگ از داخل ربات غیرفعال است."
        ),
        show_alert=True,
    )


# ============================================================
# Bot settings
# ============================================================

@router.callback_query(F.data == "admin_bot_settings")
async def admin_bot_settings(
    callback: CallbackQuery,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    support = str(
        getattr(
            settings,
            "SUPPORT_USERNAME",
            "—",
        )
        or "—"
    )

    await _safe_edit(
        callback,
        (
            "⚙️ <b>تنظیمات ربات</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            f"پشتیبانی: <code>{escape(support)}</code>\n"
            f"تعداد مدیران Config: <code>{len(_admin_ids())}</code>\n\n"
            "🔐 دسترسی مدیر فقط از ADMIN_IDS خوانده می‌شود.\n"
            "برای جلوگیری از تغییر ناامن تنظیمات حساس، "
            "Tokenها و Admin IDها از داخل تلگرام ویرایش نمی‌شوند."
        ),
        _back_admin_keyboard(),
    )

    await _safe_answer(
        callback
    )


# ============================================================
# Pending receipts
# ============================================================

@router.callback_query(F.data == "admin_pending_receipts")
async def admin_pending_receipts(
    callback: CallbackQuery,
    db: DatabaseManager,
) -> None:
    if not await _guard_callback(
        callback
    ):
        return

    path = _db_path(
        db
    )

    try:
        rows = await asyncio.to_thread(
            _sqlite_rows,
            path,
            """
            SELECT
                tx_id,
                user_id,
                amount_toman,
                status,
                gateway,
                created_at
            FROM transactions
            WHERE LOWER(
                COALESCE(status, '')
            ) IN (
                'pending',
                'waiting',
                'review'
            )
            ORDER BY created_at DESC
            LIMIT 20
            """,
        )

    except sqlite3.OperationalError:
        rows = []

    if rows:
        lines = []

        for row in rows:
            lines.append(
                (
                    f"• <code>{escape(str(row.get('tx_id') or '—'))}</code> | "
                    f"user=<code>{row.get('user_id')}</code> | "
                    f"<code>{_fmt_int(row.get('amount_toman'))}</code> تومان | "
                    f"{escape(str(row.get('status') or '—'))}"
                )
            )

        text = (
            "🧾 <b>تراکنش‌های معلق</b>\n"
            "━━━━━━━━━━━━━━━━━━━━\n\n"
            + "\n\n".join(
                lines
            )
        )

    else:
        text = (
            "🧾 <b>تراکنش‌های معلق</b>\n\n"
            "موردی برای بررسی وجود ندارد."
        )

    await _safe_edit(
        callback,
        text,
        _back_and_refresh_keyboard(
            "admin_pending_receipts"
        ),
    )

    await _safe_answer(
        callback
    )


__all__ = [
    "router",
    "AdminActionStates",
]