<?php
/**
 * ماژول «شماره مجازی» (Callinoo/Ozvinoo) — کاملا مستقل، namespace: vn*
 * ------------------------------------------------------------
 * این ماژول فقط به دکمه‌ی «📱 شماره مجازی» در «🖥 صفحه دوم | خدمات مجازی»
 * (ماژول virtual_services.php) متصل می‌شود و از همان کیف‌پول (Balance) کاربر
 * و همان سیستم قوانین/دیتابیس استفاده می‌کند. سیستم‌های Stars/Premium/گیفت
 * دست‌نخورده باقی مانده‌اند.
 *
 * سیم‌کشی (به‌صورت خودکار در همین تحویل انجام شده):
 *   index.php  : require_once + elseif (vnShouldHandle()) { vnDispatch(); }
 *   admin.php  : elseif (vnAdminShouldHandle()) { vnAdminDispatch(); }
 *   virtual_services.php        : دکمه‌ی «📱 شماره مجازی» در ردیف سوم منو
 *   virtual_services_admin.php  : دکمه‌ی ورود به تنظیمات + رنگ/ایموجی دکمه
 */

require_once __DIR__ . '/callinoo_client.php';

define('VN_ADMIN_BUTTON_TEXT', '📱 تنظیمات شماره مجازی');
define('VN_PER_PAGE', 8);
define('VN_COUNTRY_PER_PAGE', 10);

#-----------------[ جدول سفارش‌های شماره مجازی ]-------------------#
function vnEnsureTables()
{
    global $pdo;
    static $done = false;
    if ($done) return;
    $done = true;

    $pdo->exec("CREATE TABLE IF NOT EXISTS virtual_number_orders (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id BIGINT NOT NULL,
        username VARCHAR(255) NULL,
        service_id VARCHAR(64) NOT NULL,
        service_name VARCHAR(255) NULL,
        country VARCHAR(64) NOT NULL,
        number VARCHAR(64) NULL,
        request_id VARCHAR(191) NULL,
        price BIGINT NOT NULL DEFAULT 0,
        status VARCHAR(20) NOT NULL DEFAULT 'active',
        code VARCHAR(64) NULL,
        poll_attempts INT NOT NULL DEFAULT 0,
        admin_note VARCHAR(255) NULL,
        created_at DATETIME NOT NULL,
        updated_at DATETIME NULL,
        INDEX(user_id), INDEX(status), INDEX(request_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
}

#-----------------[ تنظیمات (روی همان جدول virtual_settings ماژول خدمات مجازی) ]-------------------#
function vnDefaultSettings()
{
    return [
        'vn_enabled'              => '1',
        'vn_token'                => '',
        'vn_api_url'              => '',
        'vn_price_multiplier'     => '1',
        'vn_max_poll_attempts'    => '20',
        'vn_poll_timeout_minutes' => '20',
    ];
}

function vnSetting($name, $default = null)
{
    // از همان انبار کلید/مقدار ماژول خدمات مجازی استفاده می‌شود تا دو جدول
    // موازی برای تنظیمات ساخته نشود.
    $val = function_exists('vsSetting') ? vsSetting($name, null) : null;
    if ($val !== null) return $val;
    $defaults = vnDefaultSettings();
    return $defaults[$name] ?? $default;
}

function vnSetSetting($name, $value)
{
    if (function_exists('vsSetSetting')) {
        vsSetSetting($name, $value);
    }
}

function vnApiUrl()
{
    global $callinoo_api_url;
    $override = trim((string) vnSetting('vn_api_url', ''));
    if ($override !== '') return $override;
    return isset($callinoo_api_url) ? $callinoo_api_url : '';
}

function vnToken()
{
    global $callinoo_token;
    $override = trim((string) vnSetting('vn_token', ''));
    if ($override !== '') return $override;
    return isset($callinoo_token) ? $callinoo_token : '';
}

function vnClient()
{
    return new CallinooClient(vnApiUrl(), vnToken());
}

#-----------------[ استخراج/نرمال‌سازی پاسخ API (بدون حدس‌زدن Endpoint جدید) ]-------------------#
function vnExtractList($data, $preferredKeys = ['data', 'result', 'list', 'items'])
{
    if (is_array($data) && isset($data[0]) && is_array($data[0])) {
        return $data;
    }
    foreach ($preferredKeys as $k) {
        if (isset($data[$k]) && is_array($data[$k])) {
            return $data[$k];
        }
    }
    foreach ($data as $v) {
        if (is_array($v) && isset($v[0]) && is_array($v[0])) {
            return $v;
        }
    }
    return [];
}

function vnNormalizeApp($item)
{
    if (!is_array($item)) $item = [];
    $id    = $item['id'] ?? $item['service_id'] ?? $item['code'] ?? '';
    $title = $item['title'] ?? $item['name'] ?? $item['service'] ?? (string) $id;
    return ['id' => (string) $id, 'title' => (string) $title];
}

function vnNormalizeCountry($item)
{
    if (!is_array($item)) $item = [];
    // طبق پاسخ واقعی API (تایید شده روی سرویس Ozvinoo): کد کشوری که باید در
    // Endpoint های getNumber/getCode فرستاده شود از فیلد "range" می‌آید (نه از
    // متن نمایشی "country")؛ "country" فقط برچسب نمایشی (نام + پرچم) است و
    // "count" یک عدد نیست، بلکه خودِ متن وضعیت است: «✅ موجود» یا «❌ ناموجود».
    $countryCode = $item['range'] ?? $item['country_code'] ?? $item['id'] ?? '';
    $label       = $item['country'] ?? $item['country_name'] ?? $item['name'] ?? $item['title'] ?? (string) $countryCode;
    $price       = $item['price'] ?? $item['cost'] ?? 0;
    $statusRaw   = $item['count'] ?? $item['available'] ?? $item['status'] ?? null;
    return [
        'country'    => (string) $countryCode,
        'label'      => trim((string) $label),
        'price'      => $price,
        'status_raw' => $statusRaw,
    ];
}

#-----------------[ وضعیت موقت انتخاب کاربر (روی همان فیلد Processing_value) ]-------------------#
// این وضعیت فقط بین انتخاب سرویس/کشور تا لحظه‌ی خرید استفاده می‌شود. به محض
// خرید موفق یا انصراف پاک می‌شود؛ داده‌ی سفارش نهایی همیشه از جدول
// virtual_number_orders و با ارجاع مستقیم به شناسه‌ی سفارش خوانده می‌شود، نه
// از این وضعیت موقت، تا هیچ سفارشی بین کاربران/سشن‌ها قاطی نشود.
function vnState($from_id)
{
    global $user;
    $raw = $user['Processing_value'] ?? '';
    $data = json_decode($raw, true);
    return (is_array($data) && !empty($data['_vn'])) ? $data : null;
}

function vnSaveState($from_id, array $state)
{
    global $user;
    $state['_vn'] = true;
    $encoded = json_encode($state, JSON_UNESCAPED_UNICODE);
    update('user', 'Processing_value', $encoded, 'id', $from_id);
    // مهم: $user فقط یک‌بار در ابتدای هر ریکوئست از دیتابیس خوانده می‌شود، پس
    // باید نسخه‌ی داخل حافظه را هم همین‌جا به‌روز کنیم؛ در غیر این صورت فراخوانیِ
    // بلافاصله‌ی vnState() در همین ریکوئست (مثلاً بعد از انتخاب سرویس) مقدار
    // قدیمی را می‌بیند و خطای «ابتدا یک سرویس انتخاب کنید» به‌اشتباه نمایش داده می‌شود.
    $user['Processing_value'] = $encoded;
}

function vnClearState($from_id)
{
    global $user;
    update('user', 'Processing_value', '0', 'id', $from_id);
    $user['Processing_value'] = '0';
}

#-----------------[ کیبورد صفحه‌بندی‌شده‌ی عمومی ]-------------------#
function vnPaginatedKeyboard(array $items, $page, $perPage, callable $labelFn, $idxPrefix, $pagePrefix, $backCallback, $backLabel = '🔙 بازگشت')
{
    $total = count($items);
    $page  = max(0, $page);
    $start = $page * $perPage;
    $slice = array_slice($items, $start, $perPage, true);

    $rows = [];
    $row  = [];
    foreach ($slice as $i => $it) {
        $row[] = ['text' => $labelFn($it), 'callback_data' => $idxPrefix . $i];
        if (count($row) === 2) {
            $rows[] = $row;
            $row = [];
        }
    }
    if ($row) $rows[] = $row;

    $navRow = [];
    if ($page > 0) {
        $navRow[] = ['text' => '◀️ قبلی', 'callback_data' => $pagePrefix . ($page - 1)];
    }
    if ($start + $perPage < $total) {
        $navRow[] = ['text' => 'بعدی ▶️', 'callback_data' => $pagePrefix . ($page + 1)];
    }
    if ($navRow) $rows[] = $navRow;

    $rows[] = [['text' => $backLabel, 'callback_data' => $backCallback]];

    return json_encode(['inline_keyboard' => $rows], JSON_UNESCAPED_UNICODE);
}

#-----------------[ Dispatcher سمت کاربر ]-------------------#
function vnShouldHandle()
{
    global $datain, $user;
    if (is_string($datain) && strpos($datain, 'vn') === 0) return true;
    $step = $user['step'] ?? '';
    if (is_string($step) && strpos($step, 'vn_') === 0) return true;
    return false;
}

function vnDispatch()
{
    global $from_id, $message_id, $datain, $username;

    vnEnsureTables();

    if ($datain !== 'vnmenu' && vnSetting('vn_enabled', '1') !== '1') {
        Editmessagetext($from_id, $message_id, '⛔️ بخش شماره مجازی در حال حاضر غیرفعال است.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }

    if ($datain === 'vnnoop') {
        return; // دکمه‌ی برچسبیِ غیرفعال (تیتر جدول / قیمت / وضعیت / شماره صفحه)
    }

    if ($datain === 'vnmenu') {
        if (vnSetting('vn_enabled', '1') !== '1') {
            Editmessagetext($from_id, $message_id, '⛔️ بخش شماره مجازی در حال حاضر غیرفعال است.', vsBackToMenuKeyboard(), 'HTML');
            return;
        }
        vnClearState($from_id);
        vnShowServices($from_id, $message_id, 0);
        return;
    }
    if (preg_match('/^vnsvcpageidx_(\d+)$/', $datain, $m)) {
        vnShowServices($from_id, $message_id, intval($m[1]));
        return;
    }
    if (preg_match('/^vnsvcidx_(\d+)$/', $datain, $m)) {
        vnSelectService($from_id, $message_id, intval($m[1]));
        return;
    }
    if ($datain === 'vnbacktoservices') {
        $state = vnState($from_id);
        $apps = $state['apps'] ?? null;
        if ($apps) {
            vnSaveState($from_id, ['step' => 'services', 'apps' => $apps]);
        } else {
            vnClearState($from_id);
        }
        vnShowServices($from_id, $message_id, 0);
        return;
    }
    if (preg_match('/^vncntpageidx_(\d+)$/', $datain, $m)) {
        vnShowCountries($from_id, $message_id, intval($m[1]));
        return;
    }
    if (preg_match('/^vncntidx_(\d+)$/', $datain, $m)) {
        vnSelectCountry($from_id, $message_id, intval($m[1]));
        return;
    }
    if ($datain === 'vnconfirm') {
        vnConfirmPurchase($from_id, $message_id, $username);
        return;
    }
    if ($datain === 'vncancel' || $datain === 'vnback') {
        vnClearState($from_id);
        if (function_exists('vsMenuKeyboard')) {
            Editmessagetext($from_id, $message_id, "🖥 <b>صفحه دوم | خدمات مجازی</b>\n\nیکی از خدمات زیر را انتخاب کنید:", vsMenuKeyboard(), 'HTML');
        }
        return;
    }
    if (preg_match('/^vncode_(\d+)$/', $datain, $m)) {
        vnPollCode($from_id, $message_id, intval($m[1]));
        return;
    }
    if (preg_match('/^vnlogout_(\d+)$/', $datain, $m)) {
        vnDoLogout($from_id, $message_id, intval($m[1]));
        return;
    }
}

#-----------------[ مرحله ۱: انتخاب سرویس ]-------------------#
function vnShowServices($from_id, $message_id, $page)
{
    $state = vnState($from_id);
    if (!$state || ($state['step'] ?? '') !== 'services' || empty($state['apps'])) {
        $res = vnClient()->get_applications();
        if (!$res['ok']) {
            Editmessagetext($from_id, $message_id, vnErrorMessage($res['error_type']), vsBackToMenuKeyboard(), 'HTML');
            return;
        }
        $list = vnExtractList($res['data'], ['applications', 'data', 'result', 'list', 'items']);
        $apps = array_values(array_filter(array_map('vnNormalizeApp', $list), fn($a) => $a['id'] !== ''));
        if (!$apps) {
            Editmessagetext($from_id, $message_id, '❌ در حال حاضر هیچ سرویسی برای شماره مجازی در دسترس نیست.', vsBackToMenuKeyboard(), 'HTML');
            return;
        }
        $state = ['step' => 'services', 'apps' => $apps];
        vnSaveState($from_id, $state);
    }

    $kb = vnPaginatedKeyboard(
        $state['apps'], $page, VN_PER_PAGE,
        fn($a) => $a['title'],
        'vnsvcidx_', 'vnsvcpageidx_', 'vnback'
    );
    Editmessagetext($from_id, $message_id, "[5471960722206366390] <b>شماره مجازی</b>\n\n[6026254856237356138]سرویس مورد نظر خود را انتخاب کنید:", $kb, 'HTML');
}

#-----------------[ مرحله ۲: انتخاب کشور ]-------------------#
function vnSelectService($from_id, $message_id, $idx)
{
    $state = vnState($from_id);
    if (!$state || empty($state['apps'][$idx])) {
        Editmessagetext($from_id, $message_id, '❌ این سرویس دیگر معتبر نیست. لطفاً دوباره تلاش کنید.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    $app = $state['apps'][$idx];

    $res = vnClient()->get_prices($app['id']);
    if (!$res['ok']) {
        Editmessagetext($from_id, $message_id, vnErrorMessage($res['error_type']), vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    $list = vnExtractList($res['data'], ['prices', 'countries', 'data', 'result', 'list', 'items']);
    $countries = array_values(array_filter(array_map('vnNormalizeCountry', $list), fn($c) => $c['country'] !== ''));
    if (!$countries) {
        Editmessagetext($from_id, $message_id, '❌ برای این سرویس در حال حاضر کشوری موجود نیست.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }

    vnSaveState($from_id, [
        'step'         => 'countries',
        'apps'         => $state['apps'],
        'service_id'   => $app['id'],
        'service_name' => $app['title'],
        'countries'    => $countries,
    ]);
    vnShowCountries($from_id, $message_id, 0);
}

function vnShowCountries($from_id, $message_id, $page)
{
    $state = vnState($from_id);
    if (!$state || ($state['step'] ?? '') !== 'countries' || empty($state['countries'])) {
        Editmessagetext($from_id, $message_id, '❌ ابتدا یک سرویس انتخاب کنید.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    $mult = floatval(vnSetting('vn_price_multiplier', '1'));
    if ($mult <= 0) $mult = 1;

    $kb = vnCountriesTableKeyboard($state['countries'], $page, VN_COUNTRY_PER_PAGE, $mult);
    Editmessagetext($from_id, $message_id, "📱 <b>" . htmlspecialchars($state['service_name']) . "</b>\n\nکشور مورد نظر را انتخاب کنید:", $kb, 'HTML');
}

// آیا ردیف کشور موجود است؟ اگر مقدار موجودی از API مشخص نباشد، پیش‌فرض موجود
// در نظر گرفته می‌شود؛ اگر عدد/بولی صریح صفر یا false باشد، ناموجود (قرمز) است.
// آیا ردیف کشور موجود است؟ طبق پاسخ واقعی API، این وضعیت مستقیماً در همان
// فیلد «count» به‌صورت متن فارسی «✅ موجود» / «❌ ناموجود» می‌آید؛ چند فرمت
// دیگر (بولی/عددی/انگلیسی) هم به‌صورت تدافعی پشتیبانی می‌شود.
function vnIsCountryAvailable($c)
{
    $status = $c['status_raw'] ?? null;

    if ($status === null || $status === '') {
        // اگر اصلاً وضعیتی برنگشت، بر اساس قیمت تصمیم بگیر (fallback ایمن)
        $price = $c['price'] ?? null;
        return $price !== null && $price !== '' && floatval($price) > 0;
    }
    if (is_bool($status)) {
        return $status;
    }

    $s = (string) $status;
    if (strpos($s, '❌') !== false) return false;
    if (strpos($s, '✅') !== false) return true;

    $sl = mb_strtolower(trim($s));
    if ($sl === '0' || strpos($sl, 'ناموجود') !== false || strpos($sl, 'unavailable') !== false
        || $sl === 'false' || $sl === 'no') {
        return false;
    }
    if (strpos($sl, 'موجود') !== false || strpos($sl, 'available') !== false
        || $sl === 'true' || $sl === 'yes') {
        return true;
    }
    if (is_numeric($s)) {
        return floatval($s) > 0;
    }

    return true; // پیش‌فرض امن در فرمت‌های ناشناخته: موجود در نظر گرفته شود
}

// جدول رنگی کشور/قیمت/وضعیت دقیقاً مطابق نمای درخواستی: هر ردیف = یک کشور با
// سه دکمه‌ی هم‌ردیف (کشور | قیمت | وضعیت)، سبز = موجود، قرمز = ناموجود، به‌همراه
// نشانگر «صفحه X از Y».
function vnCountriesTableKeyboard(array $countries, $page, $perPage, $mult)
{
    $total = count($countries);
    $totalPages = max(1, (int) ceil($total / $perPage));
    $page = max(0, min($page, $totalPages - 1));
    $start = $page * $perPage;
    $slice = array_slice($countries, $start, $perPage, true);

    $rows = [];
    $rows[] = [
        ['text' => '🌍 کشور', 'callback_data' => 'vnnoop'],
        ['text' => '💰 قیمت', 'callback_data' => 'vnnoop'],
        ['text' => '📶 وضعیت', 'callback_data' => 'vnnoop'],
    ];

    foreach ($slice as $i => $c) {
        $available = vnIsCountryAvailable($c);
        $style = $available ? 'success' : 'danger';
        $price = intval(round(floatval($c['price']) * $mult));
        $pickCallback = $available ? ('vncntidx_' . $i) : 'vnnoop';
        $rows[] = [
            ['text' => $c['label'], 'callback_data' => $pickCallback, 'style' => $style],
            ['text' => number_format($price), 'callback_data' => $pickCallback, 'style' => $style],
            ['text' => $available ? '✅' : '❌', 'callback_data' => $pickCallback, 'style' => $style],
        ];
    }

    $navRow = [];
    if ($page > 0) $navRow[] = ['text' => '◀️ قبلی', 'callback_data' => 'vncntpageidx_' . ($page - 1)];
    $navRow[] = ['text' => 'صفحه ' . ($page + 1) . ' از ' . $totalPages, 'callback_data' => 'vnnoop'];
    if ($page + 1 < $totalPages) $navRow[] = ['text' => 'بعدی ▶️', 'callback_data' => 'vncntpageidx_' . ($page + 1)];
    $rows[] = $navRow;

    $rows[] = [['text' => '🔙 بازگشت', 'callback_data' => 'vnbacktoservices']];

    return json_encode(['inline_keyboard' => $rows], JSON_UNESCAPED_UNICODE);
}

#-----------------[ مرحله ۳: تایید و خرید ]-------------------#
function vnSelectCountry($from_id, $message_id, $idx)
{
    $state = vnState($from_id);
    if (!$state || ($state['step'] ?? '') !== 'countries' || empty($state['countries'][$idx])) {
        Editmessagetext($from_id, $message_id, '❌ این گزینه دیگر معتبر نیست. لطفاً دوباره تلاش کنید.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    $c = $state['countries'][$idx];
    if (!vnIsCountryAvailable($c)) {
        $kb = json_encode(['inline_keyboard' => [
            [['text' => '🔙 بازگشت به لیست کشورها', 'callback_data' => 'vncntpageidx_0']],
            [['text' => '🔙 بازگشت به منو', 'callback_data' => 'vnback']],
        ]], JSON_UNESCAPED_UNICODE);
        Editmessagetext($from_id, $message_id, '❌ این کشور در حال حاضر برای این سرویس موجود نیست. کشور دیگری را انتخاب کنید.', $kb, 'HTML');
        return;
    }
    $mult = floatval(vnSetting('vn_price_multiplier', '1'));
    if ($mult <= 0) $mult = 1;
    $price = intval(round(floatval($c['price']) * $mult));

    $state['step']          = 'confirm';
    $state['country']       = $c['country'];
    $state['country_label'] = $c['label'];
    $state['price']         = $price;
    vnSaveState($from_id, $state);

    $balance = intval(select('user', 'Balance', 'id', $from_id)['Balance'] ?? 0);
    $kb = json_encode(['inline_keyboard' => [[
        ['text' => '✅ تایید و پرداخت', 'callback_data' => 'vnconfirm', 'style' => 'success'],
        ['text' => '❌ انصراف', 'callback_data' => 'vncancel', 'style' => 'danger'],
    ]]], JSON_UNESCAPED_UNICODE);

    Editmessagetext($from_id, $message_id,
        "🧾 <b>پیش‌فاکتور شماره مجازی</b>\n\n📦 سرویس: " . htmlspecialchars($state['service_name']) . "\n🌍 کشور: " . htmlspecialchars($c['label'])
        . "\n💰 قیمت: " . number_format($price) . " تومان\n💳 موجودی کیف پول شما: " . number_format($balance) . " تومان"
        . "\n\nدر صورت تایید، مبلغ از کیف پول شما کسر و بلافاصله شماره خریداری می‌شود.",
        $kb, 'HTML');
}

function vnConfirmPurchase($from_id, $message_id, $username)
{
    $state = vnState($from_id);
    if (!$state || ($state['step'] ?? '') !== 'confirm') {
        Editmessagetext($from_id, $message_id, '❌ این سفارش منقضی شده است. لطفاً دوباره از منو شروع کنید.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    $serviceId    = $state['service_id'];
    $serviceName  = $state['service_name'];
    $country      = $state['country'];
    $countryLabel = $state['country_label'];
    $price        = intval($state['price']);

    $balance = intval(select('user', 'Balance', 'id', $from_id)['Balance'] ?? 0);
    if ($balance < $price) {
        Editmessagetext($from_id, $message_id,
            "❌ موجودی کیف پول شما کافی نیست.\n\n💰 موجودی فعلی: " . number_format($balance) . " تومان\n💳 مبلغ مورد نیاز: " . number_format($price) . " تومان\n\nابتدا از بخش «کیف پول» حساب خود را شارژ کنید.",
            vsBackToMenuKeyboard(), 'HTML');
        vnClearState($from_id);
        return;
    }

    // کسر موجودی → درخواست خرید شماره از API → در صورت شکست بازگشت وجه
    update('user', 'Balance', (string) ($balance - $price), 'id', $from_id);

    $res = vnClient()->get_number($serviceId, $country);

    if (!$res['ok']) {
        update('user', 'Balance', (string) $balance, 'id', $from_id);
        vnLogFailedOrder($from_id, $username, $serviceId, $serviceName, $country, $price, $res['error_type']);
        if (function_exists('vsSendGroupError')) {
            vsSendGroupError("⚠️ <b>خطای خرید شماره مجازی</b>\n\n👤 کاربر: <a href=\"tg://user?id={$from_id}\">{$from_id}</a>\n📦 سرویس: " . htmlspecialchars($serviceName) . "\n🌍 کشور: " . htmlspecialchars($country) . "\n❌ نوع خطا: " . htmlspecialchars((string) $res['error_type']) . "\n💰 مبلغ (بازگشت داده شد): " . number_format($price) . " تومان");
        }
        Editmessagetext($from_id, $message_id, vnErrorMessage($res['error_type']) . "\n\n💰 مبلغ به کیف پول شما بازگردانده شد.", vsBackToMenuKeyboard(), 'HTML');
        vnClearState($from_id);
        return;
    }

    $data      = $res['data'];
    $number    = $data['number'] ?? ($data['data']['number'] ?? null);
    $requestId = $data['request_id'] ?? ($data['data']['request_id'] ?? null);

    if (!$number || !$requestId) {
        update('user', 'Balance', (string) $balance, 'id', $from_id);
        vnLogFailedOrder($from_id, $username, $serviceId, $serviceName, $country, $price, 'invalid_response');
        if (function_exists('vsSendGroupError')) {
            vsSendGroupError("⚠️ <b>خطای خرید شماره مجازی</b>\n\n👤 کاربر: <a href=\"tg://user?id={$from_id}\">{$from_id}</a>\n📦 سرویس: " . htmlspecialchars($serviceName) . "\n🌍 کشور: " . htmlspecialchars($country) . "\n❌ نوع خطا: پاسخ نامعتبر از API\n💰 مبلغ (بازگشت داده شد): " . number_format($price) . " تومان");
        }
        Editmessagetext($from_id, $message_id, vnErrorMessage('invalid_response') . "\n\n💰 مبلغ به کیف پول شما بازگردانده شد.", vsBackToMenuKeyboard(), 'HTML');
        vnClearState($from_id);
        return;
    }

    $orderId = vnCreateOrder($from_id, $username, $serviceId, $serviceName, $country, $number, $requestId, $price);
    vnClearState($from_id);

    $kb = json_encode(['inline_keyboard' => [
        [['text' => '🔄 دریافت کد', 'callback_data' => 'vncode_' . $orderId, 'style' => 'success']],
        [['text' => '🔚 پایان و آزادسازی شماره', 'callback_data' => 'vnlogout_' . $orderId, 'style' => 'danger']],
        [['text' => '🔙 بازگشت به منو', 'callback_data' => 'vnback']],
    ]], JSON_UNESCAPED_UNICODE);
    Editmessagetext($from_id, $message_id,
        "✅ <b>شماره با موفقیت خریداری شد</b>\n\n📦 سرویس: " . htmlspecialchars($serviceName) . "\n🌍 کشور: " . htmlspecialchars($countryLabel)
        . "\n📞 شماره: <code>" . htmlspecialchars($number) . "</code>\n🆔 شماره سفارش: #{$orderId}"
        . "\n\nپس از دریافت پیامک روی «دریافت کد» بزنید.",
        $kb, 'HTML');
}

#-----------------[ ثبت سفارش (موفق/ناموفق) ]-------------------#
function vnCreateOrder($from_id, $username, $serviceId, $serviceName, $country, $number, $requestId, $price)
{
    global $pdo;
    vnEnsureTables();
    $stmt = $pdo->prepare("INSERT INTO virtual_number_orders
        (user_id, username, service_id, service_name, country, number, request_id, price, status, created_at)
        VALUES (:u,:un,:s,:sn,:c,:num,:rid,:p,'active', NOW())");
    $stmt->execute([
        'u' => $from_id, 'un' => $username, 's' => $serviceId, 'sn' => $serviceName,
        'c' => $country, 'num' => $number, 'rid' => $requestId, 'p' => $price,
    ]);
    $orderId = $pdo->lastInsertId();

    if (function_exists('sendMemberOrderReportChannel')) {
        $timejalali = function_exists('jdate') ? jdate('Y/m/d H:i') : date('Y-m-d H:i');
        sendMemberOrderReportChannel($serviceName . ' (' . $country . ')', '1', '-', number_format($price) . ' تومان', $timejalali, $from_id, 'شماره مجازی');
    }
    return $orderId;
}

function vnLogFailedOrder($from_id, $username, $serviceId, $serviceName, $country, $price, $errorType)
{
    global $pdo;
    vnEnsureTables();
    $stmt = $pdo->prepare("INSERT INTO virtual_number_orders
        (user_id, username, service_id, service_name, country, price, status, admin_note, created_at, updated_at)
        VALUES (:u,:un,:s,:sn,:c,:p,'failed',:note, NOW(), NOW())");
    $stmt->execute([
        'u' => $from_id, 'un' => $username, 's' => $serviceId, 'sn' => $serviceName,
        'c' => $country, 'p' => $price, 'note' => 'purchase_failed:' . $errorType,
    ]);
}

#-----------------[ دریافت کد (Polling محدود، نه بی‌نهایت) ]-------------------#
function vnPollCode($from_id, $message_id, $orderId)
{
    global $pdo;
    vnEnsureTables();

    $stmt = $pdo->prepare('SELECT * FROM virtual_number_orders WHERE id=? AND user_id=?');
    $stmt->execute([$orderId, $from_id]);
    $order = $stmt->fetch();
    if (!$order) {
        Editmessagetext($from_id, $message_id, '❌ این سفارش یافت نشد.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    if ($order['status'] === 'closed') {
        Editmessagetext($from_id, $message_id, 'این سفارش قبلاً بسته شده است.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    if (!empty($order['code'])) {
        vnShowCodeScreen($from_id, $message_id, $order);
        return;
    }

    $maxAttempts = max(1, intval(vnSetting('vn_max_poll_attempts', '20')));
    $timeoutMin  = max(1, intval(vnSetting('vn_poll_timeout_minutes', '20')));
    $ageMinutes  = (time() - strtotime($order['created_at'])) / 60;

    if ($order['poll_attempts'] >= $maxAttempts || $ageMinutes >= $timeoutMin) {
        $pdo->prepare("UPDATE virtual_number_orders SET status='timeout', updated_at=NOW() WHERE id=?")->execute([$orderId]);
        $kb = json_encode(['inline_keyboard' => [
            [['text' => '🔚 آزادسازی شماره', 'callback_data' => 'vnlogout_' . $orderId, 'style' => 'danger']],
            [['text' => '🔙 بازگشت به منو', 'callback_data' => 'vnback']],
        ]], JSON_UNESCAPED_UNICODE);
        Editmessagetext($from_id, $message_id, "⏱ مهلت دریافت کد برای این شماره به پایان رسید.\n\nمی‌توانید شماره را آزاد کنید یا با پشتیبانی تماس بگیرید.", $kb, 'HTML');
        return;
    }

    $client = vnClient();
    $code = null;
    $lastErrorType = null;

    // حداکثر ۳ تلاش سریع پشت سر هم در همین درخواست (جمعاً چند ثانیه)؛ اگر باز
    // هم کدی نیامد، کاربر با دکمه‌ی «بررسی مجدد» تلاش بعدی را خودش می‌زند.
    for ($i = 0; $i < 3; $i++) {
        $pdo->prepare('UPDATE virtual_number_orders SET poll_attempts = poll_attempts + 1 WHERE id=?')->execute([$orderId]);

        $res = $client->get_code($order['request_id']);
        if ($res['ok']) {
            $c = $res['data']['code'] ?? ($res['data']['data']['code'] ?? null);
            if (!empty($c)) {
                $code = $c;
                break;
            }
            $lastErrorType = 'code_pending';
        } else {
            $lastErrorType = $res['error_type'];
            if ($lastErrorType === 'invalid_request') break;
        }

        $stmtA = $pdo->prepare('SELECT poll_attempts FROM virtual_number_orders WHERE id=?');
        $stmtA->execute([$orderId]);
        if (intval($stmtA->fetchColumn()) >= $maxAttempts) break;

        if ($i < 2) usleep(1500000); // ~۱.۵ ثانیه بین تلاش‌های خودکار
    }

    if ($code) {
        $pdo->prepare("UPDATE virtual_number_orders SET code=?, status='delivered', updated_at=NOW() WHERE id=?")->execute([$code, $orderId]);
        $order['code'] = $code;
        vnShowCodeScreen($from_id, $message_id, $order);
        return;
    }

    $stmtA = $pdo->prepare('SELECT poll_attempts FROM virtual_number_orders WHERE id=?');
    $stmtA->execute([$orderId]);
    $attempts = intval($stmtA->fetchColumn());

    $kb = json_encode(['inline_keyboard' => [
        [['text' => '🔄 بررسی مجدد', 'callback_data' => 'vncode_' . $orderId, 'style' => 'success']],
        [['text' => '🔚 آزادسازی شماره', 'callback_data' => 'vnlogout_' . $orderId, 'style' => 'danger']],
        [['text' => '🔙 بازگشت به منو', 'callback_data' => 'vnback']],
    ]], JSON_UNESCAPED_UNICODE);
    $msg = vnErrorMessage($lastErrorType ?: 'code_pending')
        . "\n\n📞 شماره: <code>" . htmlspecialchars($order['number']) . "</code>\n🔁 تعداد تلاش: {$attempts}/{$maxAttempts}";
    Editmessagetext($from_id, $message_id, $msg, $kb, 'HTML');
}

function vnShowCodeScreen($from_id, $message_id, $order)
{
    $kb = json_encode(['inline_keyboard' => [
        [['text' => '🔚 پایان و آزادسازی شماره', 'callback_data' => 'vnlogout_' . $order['id'], 'style' => 'success']],
        [['text' => '🔙 بازگشت به منو', 'callback_data' => 'vnback']],
    ]], JSON_UNESCAPED_UNICODE);
    Editmessagetext($from_id, $message_id,
        "✅ <b>کد دریافت شد</b>\n\n📞 شماره: <code>" . htmlspecialchars($order['number']) . "</code>\n🔑 کد: <code>" . htmlspecialchars($order['code']) . "</code>"
        . "\n\nپس از استفاده از کد، برای آزادسازی شماره روی دکمه زیر بزنید.",
        $kb, 'HTML');
}

#-----------------[ Logout / آزادسازی شماره ]-------------------#
function vnDoLogout($from_id, $message_id, $orderId)
{
    global $pdo;
    vnEnsureTables();

    $stmt = $pdo->prepare('SELECT * FROM virtual_number_orders WHERE id=? AND user_id=?');
    $stmt->execute([$orderId, $from_id]);
    $order = $stmt->fetch();
    if (!$order) {
        Editmessagetext($from_id, $message_id, '❌ این سفارش یافت نشد.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }
    if ($order['status'] === 'closed') {
        Editmessagetext($from_id, $message_id, 'این سفارش قبلاً بسته شده است.', vsBackToMenuKeyboard(), 'HTML');
        return;
    }

    if (!empty($order['request_id'])) {
        vnClient()->logout($order['request_id']); // best-effort؛ حتی در صورت شکست، سفارش را می‌بندیم
    }
    $pdo->prepare("UPDATE virtual_number_orders SET status='closed', updated_at=NOW() WHERE id=?")->execute([$orderId]);
    Editmessagetext($from_id, $message_id, "🔚 شماره <code>" . htmlspecialchars($order['number']) . "</code> آزاد شد و سفارش بسته شد.", vsBackToMenuKeyboard(), 'HTML');
}

#################################################################
#                     پنل مدیریت (سمت ادمین)                     #
#################################################################

function vnAdminKeyboard()
{
    return json_encode([
        'keyboard' => [
            [['text' => '🔑 تنظیم توکن API']],
            [['text' => '🌐 آدرس API'], ['text' => '📶 تست اتصال / سرویس‌ها']],
            [['text' => '💵 ضریب قیمت'], ['text' => '⏱ تنظیم Polling']],
            [['text' => '🧪 پاسخ خام قیمت‌ها']],
            [['text' => '🧾 سفارش‌های فعال'], ['text' => '⚙️ فعال/غیرفعال شماره مجازی']],
            [['text' => '🔙 بازگشت']],
        ],
        'resize_keyboard' => true,
    ]);
}

function vnAdminShouldHandle()
{
    global $text, $datain, $user, $admin_ids, $from_id;
    if (!in_array($from_id, $admin_ids ?? [])) return false;
    if ($text === VN_ADMIN_BUTTON_TEXT) return true;
    if (is_string($datain) && strpos($datain, 'vnadmin') === 0) return true;
    $step = $user['step'] ?? '';
    if (is_string($step) && strpos($step, 'vnadmin_') === 0) return true;
    $texts = ['🔑 تنظیم توکن API', '🌐 آدرس API', '📶 تست اتصال / سرویس‌ها', '💵 ضریب قیمت', '⏱ تنظیم Polling', '🧪 پاسخ خام قیمت‌ها', '🧾 سفارش‌های فعال', '⚙️ فعال/غیرفعال شماره مجازی', '🔙 بازگشت'];
    if (in_array($text, $texts, true)) return true;
    return false;
}

function vnAdminDispatch()
{
    global $text, $datain, $from_id, $message_id, $user, $pdo, $vsAdminKeyboard;

    vnEnsureTables();

    if ($text === VN_ADMIN_BUTTON_TEXT) {
        sendmessage($from_id, "📱 <b>تنظیمات شماره مجازی (Callinoo/Ozvinoo)</b>\n\nوضعیت فعلی: " . (vnSetting('vn_enabled', '1') === '1' ? 'فعال ✅' : 'غیرفعال ❌'), vnAdminKeyboard(), 'HTML');
        step('home', $from_id);
        return;
    }
    if ($text === '🔙 بازگشت') {
        sendmessage($from_id, '🔙 بازگشت به پنل خدمات مجازی.', $vsAdminKeyboard, 'HTML');
        step('home', $from_id);
        return;
    }

    #--- فعال/غیرفعال ---#
    if ($text === '⚙️ فعال/غیرفعال شماره مجازی') {
        $cur = vnSetting('vn_enabled', '1');
        $new = $cur === '1' ? '0' : '1';
        vnSetSetting('vn_enabled', $new);
        sendmessage($from_id, 'وضعیت فعلی شماره مجازی: ' . ($new === '1' ? 'فعال ✅' : 'غیرفعال ❌'), vnAdminKeyboard(), 'HTML');
        return;
    }

    #--- توکن ---#
    if ($text === '🔑 تنظیم توکن API') {
        $has = trim((string) vnSetting('vn_token', '')) !== '';
        sendmessage($from_id, ($has ? '✅ توکن قبلاً تنظیم شده است.' : '⚠️ هنوز توکنی تنظیم نشده است.') . "\n\nتوکن جدید API شماره مجازی (Ozvinoo) را ارسال کنید:\n\n<i>پس از ثبت، پیشنهاد می‌شود همین پیام را از چت پاک کنید.</i>", null, 'HTML');
        step('vnadmin_settoken', $from_id);
        return;
    }
    if ($datain === '' && ($user['step'] ?? '') === 'vnadmin_settoken') {
        $val = trim($text);
        if ($val === '') {
            sendmessage($from_id, 'توکن نمی‌تواند خالی باشد.', vnAdminKeyboard(), 'HTML');
            step('home', $from_id);
            return;
        }
        vnSetSetting('vn_token', $val);
        sendmessage($from_id, '✅ توکن با موفقیت ذخیره شد.', vnAdminKeyboard(), 'HTML');
        step('home', $from_id);
        return;
    }

    #--- آدرس API ---#
    if ($text === '🌐 آدرس API') {
        $cur = vnApiUrl();
        sendmessage($from_id, "آدرس فعلی: <code>" . htmlspecialchars($cur ?: '(تنظیم نشده)') . "</code>\n\nآدرس جدید API را ارسال کنید (یا عدد 0 برای بازگشت به مقدار پیش‌فرض تنظیمات):", null, 'HTML');
        step('vnadmin_seturl', $from_id);
        return;
    }
    if ($datain === '' && ($user['step'] ?? '') === 'vnadmin_seturl') {
        $val = trim($text);
        vnSetSetting('vn_api_url', $val === '0' ? '' : $val);
        sendmessage($from_id, '✅ آدرس API ذخیره شد.', vnAdminKeyboard(), 'HTML');
        step('home', $from_id);
        return;
    }

    #--- تست اتصال ---#
    if ($text === '📶 تست اتصال / سرویس‌ها') {
        $res = vnClient()->get_applications();
        if (!$res['ok']) {
            sendmessage($from_id, '❌ اتصال ناموفق: ' . vnErrorMessage($res['error_type']), vnAdminKeyboard(), 'HTML');
            return;
        }
        $list = vnExtractList($res['data'], ['applications', 'data', 'result', 'list', 'items']);
        $apps = array_map('vnNormalizeApp', $list);
        $lines = array_slice(array_map(fn($a) => "• " . htmlspecialchars($a['title']) . " (id: " . htmlspecialchars($a['id']) . ")", $apps), 0, 30);
        sendmessage($from_id, "✅ اتصال موفق بود. تعداد سرویس‌ها: " . count($apps) . "\n\n" . implode("\n", $lines), vnAdminKeyboard(), 'HTML');
        return;
    }

    #--- پاسخ خام قیمت‌ها (برای شناسایی نام دقیق فیلد موجودی) ---#
    if ($text === '🧪 پاسخ خام قیمت‌ها') {
        sendmessage($from_id, 'شناسه‌ی سرویس (service_id) را ارسال کنید تا پاسخ خام get-prices آن را ببینید. برای دیدن شناسه‌ها از «📶 تست اتصال / سرویس‌ها» استفاده کنید.', null, 'HTML');
        step('vnadmin_rawprices', $from_id);
        return;
    }
    if ($datain === '' && ($user['step'] ?? '') === 'vnadmin_rawprices') {
        $sid = trim($text);
        $res = vnClient()->get_prices($sid);
        if (!$res['ok']) {
            sendmessage($from_id, '❌ درخواست ناموفق بود: ' . vnErrorMessage($res['error_type']), vnAdminKeyboard(), 'HTML');
            step('home', $from_id);
            return;
        }
        $json = json_encode($res['data'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        $chunks = mb_str_split($json, 3500);
        foreach ($chunks as $i => $chunk) {
            sendmessage($from_id, "<code>" . htmlspecialchars($chunk) . "</code>", ($i === count($chunks) - 1) ? vnAdminKeyboard() : null, 'HTML');
        }
        step('home', $from_id);
        return;
    }

    #--- ضریب قیمت ---#
    if ($text === '💵 ضریب قیمت') {
        sendmessage($from_id, "ضریب فعلی: " . vnSetting('vn_price_multiplier', '1') . "\n\nاگر قیمتی که API برمی‌گرداند نیاز به تبدیل واحد یا اعمال سود دارد، ضریب جدید را عدد ارسال کنید (مثلاً 1 یا 1.2):", null, 'HTML');
        step('vnadmin_setmultiplier', $from_id);
        return;
    }
    if ($datain === '' && ($user['step'] ?? '') === 'vnadmin_setmultiplier') {
        $val = floatval(str_replace(',', '.', trim($text)));
        if ($val <= 0) {
            sendmessage($from_id, 'عدد نامعتبر است.', vnAdminKeyboard(), 'HTML');
            step('home', $from_id);
            return;
        }
        vnSetSetting('vn_price_multiplier', (string) $val);
        sendmessage($from_id, '✅ ضریب قیمت ذخیره شد.', vnAdminKeyboard(), 'HTML');
        step('home', $from_id);
        return;
    }

    #--- Polling ---#
    if ($text === '⏱ تنظیم Polling') {
        $kb = json_encode(['inline_keyboard' => [
            [['text' => '🔁 حداکثر تعداد تلاش', 'callback_data' => 'vnadminset_vn_max_poll_attempts']],
            [['text' => '⏳ حداکثر زمان (دقیقه)', 'callback_data' => 'vnadminset_vn_poll_timeout_minutes']],
        ]], JSON_UNESCAPED_UNICODE);
        sendmessage($from_id, 'کدام مقدار را می‌خواهید تغییر دهید؟', $kb, 'HTML');
        return;
    }
    if (preg_match('/^vnadminset_(vn_max_poll_attempts|vn_poll_timeout_minutes)$/', $datain, $m)) {
        update('user', 'Processing_value', $m[1], 'id', $from_id);
        Editmessagetext($from_id, $message_id, 'مقدار فعلی: ' . vnSetting($m[1]) . "\n\nمقدار جدید را عدد صحیح ارسال کنید:", json_encode(['inline_keyboard' => []]));
        step('vnadmin_setnumber', $from_id);
        return;
    }
    if ($datain === '' && ($user['step'] ?? '') === 'vnadmin_setnumber') {
        $val = intval(preg_replace('/[^0-9]/', '', $text));
        if ($val <= 0) {
            sendmessage($from_id, 'عدد نامعتبر است.', vnAdminKeyboard(), 'HTML');
            step('home', $from_id);
            return;
        }
        vnSetSetting($user['Processing_value'], (string) $val);
        sendmessage($from_id, '✅ ذخیره شد.', vnAdminKeyboard(), 'HTML');
        step('home', $from_id);
        return;
    }

    #--- سفارش‌های فعال ---#
    if ($text === '🧾 سفارش‌های فعال') {
        $rows = $pdo->query("SELECT * FROM virtual_number_orders WHERE status IN ('active','delivered','timeout') ORDER BY id DESC LIMIT 20")->fetchAll();
        if (!$rows) {
            sendmessage($from_id, 'سفارش فعالی وجود ندارد.', vnAdminKeyboard(), 'HTML');
            return;
        }
        $statusFa = ['active' => 'در انتظار کد', 'delivered' => 'کد دریافت شد', 'timeout' => 'مهلت تمام شد'];
        foreach ($rows as $row) {
            $st = $statusFa[$row['status']] ?? $row['status'];
            $msg = "🆔 #{$row['id']} — " . htmlspecialchars($row['service_name']) . " ({$row['country']})\n"
                . "👤 <a href=\"tg://user?id={$row['user_id']}\">{$row['user_id']}</a>\n"
                . "📞 <code>" . htmlspecialchars($row['number']) . "</code>\n"
                . "📌 وضعیت: {$st}\n💰 " . number_format($row['price']) . ' تومان';
            $kb = json_encode(['inline_keyboard' => [[
                ['text' => '🔚 بستن/آزادسازی', 'callback_data' => 'vnadminclose_' . $row['id'], 'style' => 'danger'],
            ]]], JSON_UNESCAPED_UNICODE);
            sendmessage($from_id, $msg, $kb, 'HTML');
        }
        return;
    }
    if (preg_match('/^vnadminclose_(\d+)$/', $datain, $m)) {
        $id = intval($m[1]);
        $stmt = $pdo->prepare('SELECT * FROM virtual_number_orders WHERE id=?');
        $stmt->execute([$id]);
        $order = $stmt->fetch();
        if (!$order || $order['status'] === 'closed') { return; }
        if (!empty($order['request_id'])) {
            vnClient()->logout($order['request_id']);
        }
        $pdo->prepare("UPDATE virtual_number_orders SET status='closed', updated_at=NOW() WHERE id=?")->execute([$id]);
        Editmessagetext($from_id, $message_id, "🔚 سفارش #{$id} بسته شد.", json_encode(['inline_keyboard' => []]));
        return;
    }
}
