<?php
require_once __DIR__ . '/bot_telegram_binding_functions.php';
require_once __DIR__ . '/bot_telegram_rebind_functions.php';
require_once __DIR__ . '/bot_digital_service_request_functions_step2_uploads_video.php';

require_once __DIR__ . '/bot_email_otp_functions.php';
require_once __DIR__ . '/bot_email_otp_flow.php';
require_once __DIR__ . '/bot_notification_functions.php';
require_once __DIR__ . '/bot_session_timeout_functions.php';
require_once __DIR__ . '/bot_balance_transaction_functions.php';
require_once __DIR__ . '/bot_banking_service_access_functions.php';
require_once __DIR__ . '/bot_digital_service_request_functions.php';
require_once __DIR__ . '/bot_digital_service_request_functions_step2_uploads.php';
require_once __DIR__ . '/bot_live_support_functions.php';
require_once __DIR__ . '/bot_account_opening_functions.php';
require_once __DIR__ . '/bot_feedback_poll_functions.php';
require_once __DIR__ . '/bot_blocked_account_recovery_functions.php';
require_once __DIR__ . '/bot_int_transfer_functions.php';
require_once __DIR__ . '/bot_wallet_transfer_functions.php';
require_once __DIR__ . '/vehicle_finance_services.php';
require_once __DIR__ . '/bot_telegram_direct_message_functions.php';
require_once __DIR__ . '/bot_user_feature_control_functions.php';
require_once __DIR__ . '/bot_virtual_card_receipts.php';
require_once __DIR__ . '/kyc_access_control.php';
require_once __DIR__ . '/kyc_phase2_verification_flow.php';


if (!function_exists('botHandleAccountOpeningReviewCallbackCompat')) {
    function botHandleAccountOpeningReviewCallbackCompat(PDO $pdo, array &$states, string $state_file, $chat_id, string $callbackData, int $telegram_user_id = 0, ?array $callback = null, ?array $message = null): bool
    {
        if (function_exists('botHandleAccountOpeningReviewCallback')) {
            return (bool) botHandleAccountOpeningReviewCallback($pdo, $states, $state_file, $chat_id, $callbackData, $telegram_user_id, $callback, $message);
        }
        return false;
    }
}

if (!function_exists('botHandleAccountOpeningReviewTextCompat')) {
    function botHandleAccountOpeningReviewTextCompat(PDO $pdo, array &$states, string $state_file, $chat_id, string $text, int $telegram_user_id = 0): bool
    {
        if (function_exists('botHandleAccountOpeningReviewText')) {
            return (bool) botHandleAccountOpeningReviewText($pdo, $states, $state_file, $chat_id, $text, $telegram_user_id);
        }
        return false;
    }
}

if (!function_exists('botHandleAccountOpeningReviewUploadCompat')) {
    function botHandleAccountOpeningReviewUploadCompat(PDO $pdo, array &$states, string $state_file, array $update, $chat_id): bool
    {
        if (function_exists('botHandleAccountOpeningReviewUpload')) {
            return (bool) botHandleAccountOpeningReviewUpload($pdo, $states, $state_file, $update, $chat_id);
        }
        return false;
    }
}

if (!function_exists('botGetWalletAccountByUserId')) {
    function botGetWalletAccountByUserId(PDO $pdo, int $userId): ?array
    {
        if ($userId <= 0) {
            return null;
        }

        $queries = [
            "SELECT id, name, email, mobile, account_number, balance, currency FROM users WHERE id = :user_id LIMIT 1",
            "SELECT id, name, email, mobile, account_number, balance FROM users WHERE id = :user_id LIMIT 1",
        ];

        foreach ($queries as $sql) {
            try {
                $stmt = $pdo->prepare($sql);
                $stmt->execute([':user_id' => $userId]);
                $row = $stmt->fetch(PDO::FETCH_ASSOC);
                if (is_array($row) && !empty($row)) {
                    if (!isset($row['currency']) || trim((string)$row['currency']) === '') {
                        $row['currency'] = 'EUR';
                    }
                    return $row;
                }
            } catch (Throwable $e) {
                // Try next fallback query.
            }
        }

        return null;
    }
}

if (!function_exists('botFormatWalletBalanceAmount')) {
    function botFormatWalletBalanceAmount($amount): string
    {
        $numeric = is_numeric($amount) ? (float)$amount : 0.0;
        return number_format($numeric, 2);
    }
}

if (!function_exists('botFormatWalletBalanceMessage')) {
    function botFormatWalletBalanceMessage(array $account): string
    {
        $name = htmlspecialchars((string)($account['name'] ?? 'Customer'), ENT_QUOTES, 'UTF-8');
        $accountNumber = trim((string)($account['account_number'] ?? ''));
        $accountNumber = $accountNumber !== '' ? htmlspecialchars($accountNumber, ENT_QUOTES, 'UTF-8') : 'Not Available';
        $currency = trim((string)($account['currency'] ?? 'EUR'));
        $currency = $currency !== '' ? htmlspecialchars($currency, ENT_QUOTES, 'UTF-8') : 'EUR';
        $balance = botFormatWalletBalanceAmount($account['balance'] ?? 0);

        return "💰 <b>FinoviaPay Balance Summary</b>\n\n"
            . "👤 <b>Account Holder</b>\n{$name}\n\n"
            . "🏷️ <b>Account Number</b>\n{$accountNumber}\n\n"
            . "💳 <b>Available Balance</b>\n{$balance} {$currency}\n\n"
            . "This balance reflects the latest available amount currently recorded on your FinoviaPay profile.";
    }
}

if (!function_exists('botBalanceKeyboard')) {
    function botBalanceKeyboard(): array
    {
        return [
            'inline_keyboard' => [
                [
                    ['text' => '📊 My Transactions', 'callback_data' => 'wallet_transactions']
                ],
                [
                    ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
                ]
            ]
        ];
    }
}

if (!function_exists('botGetLatestWalletTransactions')) {
    function botGetLatestWalletTransactions(PDO $pdo, int $userId, int $limit = 5): array
    {
        if ($userId <= 0) {
            return [];
        }

        $limit = max(1, min(20, $limit));

        $candidates = [
            [
                'sql' => "SELECT reference, total_debited_eur AS amount, send_amount_eur, fee_eur, status_code AS status, created_at FROM new_mw_transfers WHERE user_id = :user_id ORDER BY id DESC LIMIT {$limit}",
                'type' => 'mobile_wallet',
            ],
            [
                'sql' => "SELECT reference, amount, status, created_at FROM transactions WHERE user_id = :user_id ORDER BY id DESC LIMIT {$limit}",
                'type' => 'transactions',
            ],
            [
                'sql' => "SELECT reference, amount, status, created_at FROM wallet_transactions WHERE user_id = :user_id ORDER BY id DESC LIMIT {$limit}",
                'type' => 'wallet_transactions',
            ],
        ];

        foreach ($candidates as $candidate) {
            try {
                $stmt = $pdo->prepare($candidate['sql']);
                $stmt->execute([':user_id' => $userId]);
                $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
                if (is_array($rows) && !empty($rows)) {
                    return $rows;
                }
            } catch (Throwable $e) {
                // Try next candidate.
            }
        }

        return [];
    }
}

if (!function_exists('botFormatTransactionsMessage')) {
    function botFormatTransactionsMessage(array $transactions): string
    {
        if (empty($transactions)) {
            return "📊 <b>Recent Transactions</b>\n\nNo recent transactions are currently available for your FinoviaPay profile.";
        }

        $lines = ["📊 <b>Recent Transactions</b>", ""];

        $count = 0;
        foreach ($transactions as $tx) {
            if (++$count > 5) {
                break;
            }

            $reference = trim((string)($tx['reference'] ?? 'Not Available'));
            $reference = htmlspecialchars($reference !== '' ? $reference : 'Not Available', ENT_QUOTES, 'UTF-8');

            $amountValue = $tx['amount'] ?? ($tx['total_debited_eur'] ?? ($tx['send_amount_eur'] ?? 0));
            $amount = botFormatWalletBalanceAmount($amountValue);

            $status = strtoupper(trim((string)($tx['status'] ?? $tx['status_code'] ?? 'PENDING')));
            $status = htmlspecialchars($status !== '' ? $status : 'PENDING', ENT_QUOTES, 'UTF-8');

            $createdAtRaw = trim((string)($tx['created_at'] ?? ''));
            $createdAt = $createdAtRaw !== '' ? date('d M Y H:i', strtotime($createdAtRaw)) : 'Not Available';
            $createdAt = htmlspecialchars($createdAt, ENT_QUOTES, 'UTF-8');

            $lines[] = "• <b>Reference:</b> {$reference}";
            $lines[] = "  <b>Amount:</b> {$amount} EUR";
            $lines[] = "  <b>Status:</b> {$status}";
            $lines[] = "  <b>Date:</b> {$createdAt}";
            $lines[] = "";
        }

        return rtrim(implode("\n", $lines));
    }
}

if (!function_exists('botTransactionsKeyboard')) {
    function botTransactionsKeyboard(): array
    {
        return [
            'inline_keyboard' => [
                [
                    ['text' => '💰 Check Balance', 'callback_data' => 'wallet_balance']
                ],
                [
                    ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
                ]
            ]
        ];
    }
}


if (!function_exists('botMainEntryButtons')) {
    function botMainEntryButtons(): array
    {
        return [
            [
                ['text' => '🏛️ Existing Customer Banking', 'callback_data' => 'existing_customer_menu']
            ],
            [
                ['text' => '✨ New Customer Onboarding', 'callback_data' => 'new_customer_menu']
            ],
            [
                ['text' => '🎖️ Premium Support Centre', 'callback_data' => 'support_help_menu']
            ],
            [
                ['text' => '🛡️ Blocked Account Recovery', 'callback_data' => 'blocked_account_menu']
            ]
        ];
    }
}

if (!function_exists('botWelcomeMessage')) {
    function botWelcomeMessage(): string
    {
        return "🏛️ <b>Welcome to FinoviaPay Premium Digital Banking</b>

"
            . "Welcome to the official <b>FinoviaPay Telegram Banking Desk</b>, a secure customer service channel created to provide premium banking guidance, digital onboarding support, account assistance, and protected service access.

"
            . "<b>Premium Banking Sections</b>
"
            . "• Existing Customer Banking
"
            . "• New Customer Onboarding
"
            . "• Premium Support Centre
"
            . "• Blocked Account Recovery

"
            . "<b>Customer Notice</b>
"
            . "Selected digital banking services may be activated progressively in line with internal review, operational readiness, and secure service rollout.

"
            . "Please choose your preferred banking section below to proceed.";
    }
}

if (!function_exists('botSendWelcomeMenu')) {
    function botSendWelcomeMenu(array &$states, string $state_file, $chat_id): void
    {
        $existing = $states[$chat_id] ?? [];
        $preserveKeys = ['user', 'identified_user_id', 'entered_mobile', 'blocked_user'];
        $newState = ['step' => 'choose_customer_type'];
        foreach ($preserveKeys as $key) {
            if (array_key_exists($key, $existing)) {
                $newState[$key] = $existing[$key];
            }
        }
        $states[$chat_id] = $newState;
        saveStates($states, $state_file);
        sendInlineKeyboard($chat_id, botWelcomeMessage(), botMainEntryButtons());
    }
}

if (!function_exists('botNewCustomerMenuButtons')) {
    function botNewCustomerMenuButtons(): array
    {
        return [
            [
                ['text' => '🏦 Open a New Account', 'callback_data' => 'new_customer_open_account']
            ],
            [
                ['text' => '📋 Check Registration Status', 'callback_data' => 'new_customer_check_registration_status']
            ],
            [
                ['text' => '🎖️ Registration Assistance', 'callback_data' => 'new_customer_talk_to_support']
            ],
            [
                ['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

if (!function_exists('botSupportHelpMenuButtons')) {
    function botSupportHelpMenuButtons(): array
    {
        return [
            [
                ['text' => '💬 Live Support Desk', 'callback_data' => 'support_help_live_chat']
            ],
            [
                ['text' => '🎫 Open Support Ticket', 'callback_data' => 'support_help_create_ticket']
            ],
            [
                ['text' => '📩 General Banking Guidance', 'callback_data' => 'support_help_live_chat']
            ],
            [
                ['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

if (!function_exists('botBlockedAccountMenuButtons')) {
    function botBlockedAccountMenuButtons(): array
    {
        return [
            [
                ['text' => '🛡️ Submit Recovery Request', 'callback_data' => 'blocked_account_recovery']
            ],
            [
                ['text' => '🎖️ Contact Support Officer', 'callback_data' => 'talk_to_agent_unverified']
            ],
            [
                ['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

if (!function_exists('botGetSetting')) {
    function botGetSetting(PDO $pdo, string $key, string $default = 'enabled'): string
    {
        try {
            $stmt = $pdo->prepare("SELECT setting_value FROM telegram_bot_settings WHERE setting_key = :setting_key LIMIT 1");
            $stmt->execute([':setting_key' => $key]);
            $value = $stmt->fetchColumn();
            return ($value === false || $value === null || $value === '') ? $default : (string)$value;
        } catch (Throwable $e) {
            return $default;
        }
    }
}


if (!function_exists('botServiceGateLog')) {
    function botServiceGateLog(string $message): void
    {
        @file_put_contents(__DIR__ . '/bot_service_gate_debug.log', '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL, FILE_APPEND);
    }
}

if (!function_exists('botHasPendingInteractiveContext')) {
    function botHasPendingInteractiveContext(array $chatState): bool
    {
        $step = (string)($chatState['step'] ?? '');

        $idleSteps = [
            '',
            'choose_customer_type',
            'verified',
            'blocked',
            'mobile_not_found',
            'telegram_binding_restricted',
            'digital_banking_service_disabled',
        ];

        if (!in_array($step, $idleSteps, true)) {
            return true;
        }

        if (($chatState['wallet_transfer_verification_request']['step'] ?? '') === 'await_upload') {
            return true;
        }

        return false;
    }
}

if (!function_exists('startDigitalBankingEnablementTicketFlow')) {
    function startDigitalBankingEnablementTicketFlow(array &$states, string $state_file, $chat_id): void
    {
        $states[$chat_id]['step'] = 'digital_banking_enablement_ticket';
        saveStates($states, $state_file);
    }
}


/* ===============================
   KYC VERIFICATION HELPERS
================================ */

function getPendingKycRequestByUserId(PDO $pdo, int $userId): ?array {
    $stmt = $pdo->prepare("
        SELECT *
        FROM kyc_verification_requests
        WHERE user_id = :user_id
          AND status IN ('pending', 'submitted', 'rejected')
        ORDER BY id DESC
        LIMIT 20
    ");
    $stmt->execute([':user_id' => $userId]);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];

    foreach ($rows as $row) {
        if (requestHasPendingKycItems($pdo, (int)$row['id'])) {
            return $row;
        }
    }

    return null;
}

function requestHasPendingKycItems(PDO $pdo, int $requestId): bool {
    try {
        $stmt = $pdo->prepare("
            SELECT COUNT(*) AS total
            FROM kyc_verification_request_items
            WHERE request_id = :request_id
              AND status IN ('pending','failed','reupload_required')
        ");
        $stmt->execute([':request_id' => $requestId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return ((int)($row['total'] ?? 0)) > 0;
    } catch (Throwable $e) {
        return false;
    }
}

function formatKycRequestedItems(PDO $pdo, array $request): string {
    $map = [
        'selfie_verification' => 'Selfie Verification',
        'video_verification'  => 'Video Verification',
        'passport_image'      => 'Passport Image',
        'id_card_front'       => 'ID Card Front',
        'id_card_back'        => 'ID Card Back',
        'proof_of_address'    => 'Proof of Address',
        'source_of_funds'     => 'Source of Funds Document',
        'custom_document'     => 'Additional Verification Document'
    ];

    $lines = [];

    try {
        $stmt = $pdo->prepare("
            SELECT item_key, item_label, status
            FROM kyc_verification_request_items
            WHERE request_id = :request_id
              AND status IN ('pending','failed','reupload_required')
            ORDER BY id ASC
        ");
        $stmt->execute([':request_id' => (int)$request['id']]);
        $items = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];

        foreach ($items as $item) {
            $label = trim((string)($item['item_label'] ?? ''));
            if ($label === '') {
                $key = (string)($item['item_key'] ?? 'custom_document');
                $label = $map[$key] ?? $key;
            }

            $status = strtolower((string)($item['status'] ?? 'pending'));
            if (in_array($status, ['failed', 'reupload_required'], true)) {
                $label .= ' (Re-upload Required)';
            }

            $lines[] = '• ' . $label;
        }
    } catch (Throwable $e) {
        $lines = [];
    }

    if (empty($lines)) {
        $json = $request['requested_items'] ?? null;
        if (!$json) return "• Additional Verification Document";
        $items = json_decode((string)$json, true);
        if (!is_array($items) || empty($items)) return "• Additional Verification Document";
        foreach ($items as $item) {
            $lines[] = '• ' . ($map[$item] ?? $item);
        }
    }

    return implode("\n", $lines);
}

error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);

function bot_debug_log(string $message): void {
    $dir = __DIR__ . '/uploads/wallet_transfer_verification';
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }
    @file_put_contents($dir . '/bot_runtime_debug.log', '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL, FILE_APPEND);
}

register_shutdown_function(function () {
    $e = error_get_last();
    if ($e) {
        $dir = __DIR__ . '/uploads/wallet_transfer_verification';
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
        @file_put_contents($dir . '/bot_fatal_debug.log', '[' . date('Y-m-d H:i:s') . '] ' . json_encode($e, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
    }
});

require_once "config.php"; // must provide $pdo (PDO connection)

require_once __DIR__ . '/bot_token.php';

if (!isset($token) || trim((string)$token) === '') {
    die('Telegram bot token is missing.');
}
$update = json_decode(file_get_contents("php://input"), true);
file_put_contents(__DIR__.'/test_update.txt', json_encode($update).PHP_EOL, FILE_APPEND);
if (!function_exists('blocked_debug_log')) {
function blocked_debug_log($message) {
    @file_put_contents(__DIR__.'/bot_block_debug.log', '[' . date('Y-m-d H:i:s') . '] ' . $message . PHP_EOL, FILE_APPEND);
}
}

bot_debug_log("BOT HIT webhook request received");

$state_file = __DIR__ . "/users_state.json";
if (!file_exists($state_file)) {
    file_put_contents($state_file, json_encode([]));
}
$states = json_decode(file_get_contents($state_file), true);
if (!is_array($states)) {
    $states = [];
}

function saveStates($states, $state_file) {
    file_put_contents($state_file, json_encode($states, JSON_UNESCAPED_UNICODE));
}


if (!function_exists('botVirtualCardNotificationHandleCallback')) {
    function botVirtualCardNotificationHandleCallback(PDO $pdo, string $callbackData, ?string $callbackId, $chatId): bool
    {
        $callbackData = trim((string)$callbackData);
        if ($callbackData === '' || (strpos($callbackData, 'vcn_confirm|') !== 0 && strpos($callbackData, 'vcn_deny|') !== 0)) {
            return false;
        }

        $notificationId = (int)substr($callbackData, strrpos($callbackData, '|') + 1);
        $action = strpos($callbackData, 'vcn_confirm|') === 0 ? 'confirmed' : 'denied';
        $chatId = (string)$chatId;

        if ($notificationId <= 0 || $chatId === '') {
            if (!empty($callbackId)) {
                answerCallbackQuery($callbackId, 'Invalid notification response.');
            }
            return true;
        }

        try {
            $stmt = $pdo->prepare("SELECT id, customer_telegram_id, customer_response FROM bot_virtual_card_notifications WHERE id = :id LIMIT 1");
            $stmt->execute([':id' => $notificationId]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$row) {
                if (!empty($callbackId)) {
                    answerCallbackQuery($callbackId, 'Notification record not found.');
                }
                return true;
            }

            if ((string)($row['customer_telegram_id'] ?? '') !== $chatId) {
                if (!empty($callbackId)) {
                    answerCallbackQuery($callbackId, 'This response is not valid for your account.');
                }
                return true;
            }

            if ((string)($row['customer_response'] ?? 'pending') !== 'pending') {
                if (!empty($callbackId)) {
                    answerCallbackQuery($callbackId, 'Your response has already been recorded.');
                }
                return true;
            }

            $update = $pdo->prepare("UPDATE bot_virtual_card_notifications
                SET customer_response = :response,
                    customer_response_at = NOW()
                WHERE id = :id
                  AND customer_telegram_id = :chat_id
                  AND customer_response = 'pending'");
            $update->execute([
                ':response' => $action,
                ':id' => $notificationId,
                ':chat_id' => $chatId,
            ]);

            if (!empty($callbackId)) {
                answerCallbackQuery($callbackId, $action === 'confirmed' ? 'Your confirmation has been recorded.' : 'Your report has been recorded.');
            }

            $followup = $action === 'confirmed'
                ? "Thank you. Your confirmation has been recorded successfully.\n\nFinoviapay Security Monitoring"
                : "Thank you. Your report has been recorded and forwarded for review.\n\nIf this activity was unauthorized, our team may contact you shortly.\n\nFinoviapay Security Monitoring";
            sendMessage($chatId, $followup);
        } catch (Throwable $e) {
            error_log('Virtual card callback error: ' . $e->getMessage());
            if (!empty($callbackId)) {
                answerCallbackQuery($callbackId, 'Unable to record your response right now.');
            }
        }

        return true;
    }
}


if (!function_exists('botInstallJoinLogsTable')) {
    function botInstallJoinLogsTable(PDO $pdo): void
    {
        static $installed = false;
        if ($installed) {
            return;
        }

        $sql = "CREATE TABLE IF NOT EXISTS `bot_join_logs` (
            `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
            `telegram_chat_id` VARCHAR(50) NOT NULL,
            `telegram_user_id` VARCHAR(50) DEFAULT NULL,
            `first_name` VARCHAR(150) DEFAULT NULL,
            `last_name` VARCHAR(150) DEFAULT NULL,
            `username` VARCHAR(150) DEFAULT NULL,
            `language_code` VARCHAR(20) DEFAULT NULL,
            `mobile_number` VARCHAR(50) DEFAULT NULL,
            `country_name` VARCHAR(120) DEFAULT NULL,
            `country_code` VARCHAR(20) DEFAULT NULL,
            `is_bot` TINYINT(1) NOT NULL DEFAULT 0,
            `start_count` INT UNSIGNED NOT NULL DEFAULT 1,
            `first_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `last_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            `last_payload` LONGTEXT DEFAULT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `uniq_telegram_chat_id` (`telegram_chat_id`),
            KEY `idx_telegram_user_id` (`telegram_user_id`),
            KEY `idx_username` (`username`),
            KEY `idx_mobile_number` (`mobile_number`),
            KEY `idx_country_code` (`country_code`),
            KEY `idx_last_seen_at` (`last_seen_at`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
        $pdo->exec($sql);
        $installed = true;
    }
}

if (!function_exists('botLogJoinStart')) {
    function botLogJoinStart(PDO $pdo, array $update): void
    {
        try {
            botInstallJoinLogsTable($pdo);

            $message = $update['message'] ?? null;
            if (!is_array($message)) {
                return;
            }

            $chat = $message['chat'] ?? [];
            $from = $message['from'] ?? [];

            $chatId = trim((string)($chat['id'] ?? ''));
            if ($chatId === '') {
                return;
            }

            $telegramUserId = trim((string)($from['id'] ?? ''));
            $firstName = isset($from['first_name']) ? trim((string)$from['first_name']) : null;
            $lastName = isset($from['last_name']) ? trim((string)$from['last_name']) : null;
            $username = isset($from['username']) ? trim((string)$from['username']) : null;
            $languageCode = isset($from['language_code']) ? trim((string)$from['language_code']) : null;
            $isBot = !empty($from['is_bot']) ? 1 : 0;

            $payloadJson = json_encode($update, JSON_UNESCAPED_UNICODE);

            $findMobileSql = "SELECT customer_mobile AS mobile, NULL AS country_name, NULL AS country_code
                              FROM support_chats
                              WHERE telegram_id = :chat_id AND customer_mobile IS NOT NULL AND customer_mobile <> ''
                              ORDER BY id DESC LIMIT 1";
            $mobile = null;
            $countryName = null;
            $countryCode = null;

            try {
                $stmtLookup = $pdo->prepare($findMobileSql);
                $stmtLookup->execute([':chat_id' => $chatId]);
                $lookup = $stmtLookup->fetch(PDO::FETCH_ASSOC);
                if ($lookup) {
                    $mobile = $lookup['mobile'] ?? null;
                    $countryName = $lookup['country_name'] ?? null;
                    $countryCode = $lookup['country_code'] ?? null;
                }
            } catch (Throwable $e) {
                // keep graceful
            }

            $sql = "INSERT INTO bot_join_logs
                    (telegram_chat_id, telegram_user_id, first_name, last_name, username, language_code, mobile_number, country_name, country_code, is_bot, start_count, first_seen_at, last_seen_at, last_payload)
                    VALUES
                    (:telegram_chat_id, :telegram_user_id, :first_name, :last_name, :username, :language_code, :mobile_number, :country_name, :country_code, :is_bot, 1, NOW(), NOW(), :last_payload)
                    ON DUPLICATE KEY UPDATE
                        telegram_user_id = VALUES(telegram_user_id),
                        first_name = VALUES(first_name),
                        last_name = VALUES(last_name),
                        username = VALUES(username),
                        language_code = VALUES(language_code),
                        mobile_number = COALESCE(NULLIF(VALUES(mobile_number), ''), mobile_number),
                        country_name = COALESCE(NULLIF(VALUES(country_name), ''), country_name),
                        country_code = COALESCE(NULLIF(VALUES(country_code), ''), country_code),
                        is_bot = VALUES(is_bot),
                        start_count = start_count + 1,
                        last_seen_at = NOW(),
                        last_payload = VALUES(last_payload)";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([
                ':telegram_chat_id' => $chatId,
                ':telegram_user_id' => $telegramUserId !== '' ? $telegramUserId : null,
                ':first_name' => $firstName !== '' ? $firstName : null,
                ':last_name' => $lastName !== '' ? $lastName : null,
                ':username' => $username !== '' ? $username : null,
                ':language_code' => $languageCode !== '' ? $languageCode : null,
                ':mobile_number' => $mobile ? trim((string)$mobile) : null,
                ':country_name' => $countryName ? trim((string)$countryName) : null,
                ':country_code' => $countryCode ? trim((string)$countryCode) : null,
                ':is_bot' => $isBot,
                ':last_payload' => $payloadJson !== false ? $payloadJson : null,
            ]);
        } catch (Throwable $e) {
            error_log('BOT ERROR: Failed to log /start join event: ' . $e->getMessage());
        }
    }
}

function sendMessage($chat_id, $message, $reply_markup = null) {
    global $token;

    $payload = [
        'chat_id' => $chat_id,
        'text' => $message,
        'parse_mode' => 'HTML'
    ];

    if ($reply_markup) {
        $payload['reply_markup'] = json_encode($reply_markup);
    }

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot{$token}/sendMessage");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $response = curl_exec($ch);

    if ($response === false) {
        if (function_exists('bot_debug_log')) {
            bot_debug_log('sendMessage cURL error: ' . curl_error($ch));
        }
    } else {
        if (function_exists('bot_debug_log')) {
            bot_debug_log('sendMessage response: ' . $response);
        }
    }

    curl_close($ch);
}


if (!function_exists('botHandleGeneralNotificationConfirmation')) {
    function botHandleGeneralNotificationConfirmation(PDO $pdo, string $callbackData, $callbackId, $chatId): bool
    {
        if (strpos($callbackData, 'gn_confirm_') !== 0) {
            return false;
        }

        $recipientId = (int)substr($callbackData, strlen('gn_confirm_'));
        if ($recipientId <= 0) {
            if ($callbackId) {
                answerCallbackQuery($callbackId, 'Invalid notification confirmation request.');
            }
            return true;
        }

        try {
            $stmt = $pdo->prepare("SELECT id, notification_id, is_confirmed FROM telegram_general_notification_recipients WHERE id = :id LIMIT 1");
            $stmt->execute([':id' => $recipientId]);
            $recipient = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$recipient) {
                if ($callbackId) {
                    answerCallbackQuery($callbackId, 'Notification record was not found.');
                }
                return true;
            }

            if ((int)($recipient['is_confirmed'] ?? 0) !== 1) {
                $updateStmt = $pdo->prepare("\n                    UPDATE telegram_general_notification_recipients\n                    SET \n                        is_read = 1,\n                        read_at = NOW(),\n                        is_confirmed = 1,\n                        confirmed_at = NOW(),\n                        confirmation_note = 'Confirmed by inline button'\n                    WHERE id = :id\n                ");
                $updateStmt->execute([':id' => $recipientId]);

                $notificationId = (int)($recipient['notification_id'] ?? 0);
                if ($notificationId > 0) {
                    $countStmt = $pdo->prepare("SELECT COUNT(*) FROM telegram_general_notification_recipients WHERE notification_id = :notification_id AND is_confirmed = 1");
                    $countStmt->execute([':notification_id' => $notificationId]);
                    $confirmedCount = (int)$countStmt->fetchColumn();

                    $masterStmt = $pdo->prepare("UPDATE telegram_general_notifications SET total_confirmed = :total_confirmed WHERE id = :id");
                    $masterStmt->execute([
                        ':total_confirmed' => $confirmedCount,
                        ':id' => $notificationId,
                    ]);
                }
            }

            if ($callbackId) {
                answerCallbackQuery($callbackId, 'Acknowledgement recorded successfully.');
            }

            $premiumConfirmationMessage =
                "✅ <b>Thank you. Your acknowledgement has been recorded successfully.</b>\n\n"
                . "This notification has been marked as received and confirmed.\n"
                . "FinoviaPay appreciates your cooperation.\n\n"
                . "— <b>FinoviaPay System Notification</b>\n"
                . "Worldwide Digital Internet Banking";

            sendMessage(
                $chatId,
                $premiumConfirmationMessage
            );
        } catch (Throwable $e) {
            if (function_exists('bot_debug_log')) {
                bot_debug_log('General notification confirmation error: ' . $e->getMessage());
            }
            if ($callbackId) {
                answerCallbackQuery($callbackId, 'Unable to record your confirmation right now.');
            }
        }

        return true;
    }
}

function answerCallbackQuery($callback_id, $text = '') {
    global $token;

    $payload = [
        'callback_query_id' => $callback_id,
        'text' => $text
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot{$token}/answerCallbackQuery");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $response = curl_exec($ch);

    if ($response === false) {
        if (function_exists('bot_debug_log')) {
            bot_debug_log('answerCallbackQuery cURL error: ' . curl_error($ch));
        }
    } else {
        if (function_exists('bot_debug_log')) {
            bot_debug_log('answerCallbackQuery response: ' . $response);
        }
    }

    curl_close($ch);
}

function sendInlineKeyboard($chat_id, $text, $buttons) {
    sendMessage($chat_id, $text, ['inline_keyboard' => $buttons]);
}

function normalizeMobile($mobile) {
    $mobile = trim($mobile);
    return preg_replace('/\s+/', '', $mobile);
}

function normalizeDob($dob) {
    $dob = trim($dob);
    $ts = strtotime($dob);
    if ($ts === false) return false;
    return date("Y-m-d", $ts);
}

function generateTicketId() {
    return 'FP-CS-' . str_pad((string) random_int(1, 99999), 5, '0', STR_PAD_LEFT);
}

function getQueuePosition($pdo) {
    $stmt = $pdo->query("SELECT COUNT(*) AS total FROM support_chats WHERE status = 'waiting'");
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    return ((int)($row['total'] ?? 0)) + 1;
}

function estimateWaitMinutes($queuePosition) {
    $minutes = max(2, $queuePosition * 2);
    return $minutes;
}

function buildTranscriptText($pdo, $chatId) {
    $stmt = $pdo->prepare("SELECT sender, message, created_at FROM support_messages WHERE chat_id = :chat_id ORDER BY id ASC");
    $stmt->execute([':chat_id' => $chatId]);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $lines = [];
    foreach ($rows as $row) {
        $sender = strtoupper((string)($row['sender'] ?? 'SYSTEM'));
        $createdAt = (string)($row['created_at'] ?? '');
        $message = (string)($row['message'] ?? '');
        $lines[] = "[{$createdAt}] {$sender}: {$message}";
    }

    return implode("\n", $lines);
}

function generateSupportTicketId() {
    return 'FP-TK-' . str_pad((string) random_int(1, 99999), 5, '0', STR_PAD_LEFT);
}

if (!function_exists('botIsLiveChatEnabled')) {
    function botIsLiveChatEnabled(PDO $pdo): bool
    {
        try {
            $stmt = $pdo->query("SELECT chat_status FROM live_chat_control ORDER BY id DESC LIMIT 1");
            $status = $stmt->fetchColumn();
            return strtolower(trim((string)$status)) === 'enabled';
        } catch (Throwable $e) {
            return true;
        }
    }
}

if (!function_exists('botLiveChatUnavailableMessage')) {
    function botLiveChatUnavailableMessage(): string
    {
        return "We sincerely apologize for the inconvenience.\n\nAt this time, our live customer support officers are currently assisting other customers and secure live chat is temporarily unavailable.\n\nWe kindly request that you create a support ticket so that your request may be formally recorded and reviewed by the appropriate FinoviaPay support department.\n\nThank you for your patience, understanding, and continued trust in FinoviaPay.";
    }
}

if (!function_exists('botLiveChatUnavailableKeyboard')) {
    function botLiveChatUnavailableKeyboard(): array
    {
        return [
            [
                ['text' => '🎫 Create Support Ticket', 'callback_data' => 'create_support_ticket']
            ],
            [
                ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

function createSupportTicketRecord($pdo, $stateData, $cardType, $issueType, $description, $telegramId = null) {
    $user = $stateData['user'] ?? [];
    $ticketId = generateSupportTicketId();
    $userId = $user['id'] ?? null;
    $customerName = $user['name'] ?? 'Unverified Customer';
    $category = 'Card Support';
    $department = 'Card Operations Department';
    $status = 'Open';

    $stmt = $pdo->prepare("
        INSERT INTO support_tickets
        (ticket_id, user_id, customer_name, telegram_id, category, department, issue_type, description, status, created_at)
        VALUES
        (:ticket_id, :user_id, :customer_name, :telegram_id, :category, :department, :issue_type, :description, :status, NOW())
    ");
    $stmt->execute([
        ':ticket_id' => $ticketId,
        ':user_id' => $userId,
        ':customer_name' => $customerName,
        ':telegram_id' => $telegramId,
        ':category' => $category,
        ':department' => $department,
        ':issue_type' => $issueType . ' | Card Type: ' . $cardType,
        ':description' => $description,
        ':status' => $status
    ]);

    $messageStmt = $pdo->prepare("
        INSERT INTO ticket_messages (ticket_id, sender, message, created_at)
        VALUES (:ticket_id, 'customer', :message, NOW())
    ");
    $messageStmt->execute([
        ':ticket_id' => $ticketId,
        ':message' => $description
    ]);

    return $ticketId;
}

function createGeneralSupportTicketRecord($pdo, $stateData, $subject, $description, $telegramId = null) {
    $user = $stateData['user'] ?? [];
    $ticketId = generateSupportTicketId();
    $userId = $user['id'] ?? null;
    $customerName = $user['name'] ?? 'Unverified Customer';
    $category = 'General Support';
    $department = 'Customer Support Department';
    $status = 'Open';

    $stmt = $pdo->prepare("
        INSERT INTO support_tickets
        (ticket_id, user_id, customer_name, telegram_id, category, department, issue_type, description, status, created_at)
        VALUES
        (:ticket_id, :user_id, :customer_name, :telegram_id, :category, :department, :issue_type, :description, :status, NOW())
    " );
    $stmt->execute([
        ':ticket_id' => $ticketId,
        ':user_id' => $userId,
        ':customer_name' => $customerName,
        ':telegram_id' => $telegramId,
        ':category' => $category,
        ':department' => $department,
        ':issue_type' => $subject,
        ':description' => $description,
        ':status' => $status
    ]);

    $messageStmt = $pdo->prepare("
        INSERT INTO ticket_messages (ticket_id, sender, message, created_at)
        VALUES (:ticket_id, 'customer', :message, NOW())
    " );
    $messageStmt->execute([
        ':ticket_id' => $ticketId,
        ':message' => "Subject: {$subject}\n\nIssue Details:\n{$description}"
    ]);

    return $ticketId;
}


function sendMyNotificationsMenu(PDO $pdo, $chat_id, int $userId): void
{
    $notifications = botGetUserNotifications($pdo, $userId, 10);
    botMarkUserNotificationsRead($pdo, $userId);

    $message = botFormatNotificationHistoryMessage($notifications);

    $buttons = [
        [
            ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
        ]
    ];

    sendInlineKeyboard($chat_id, $message, $buttons);
}

function sendWalletBalanceMenu(PDO $pdo, $chat_id, int $userId): void
{
    $account = botGetWalletAccountByUserId($pdo, $userId);

    if (!$account) {
        sendMessage($chat_id, "💰 <b>FinoviaPay Balance Summary</b>\n\nNo wallet balance account is currently available for your verified FinoviaPay profile.");
        return;
    }

    sendMessage($chat_id, botFormatWalletBalanceMessage($account), botBalanceKeyboard());
}

function sendWalletTransactionsMenu(PDO $pdo, $chat_id, int $userId): void
{
    $transactions = botGetLatestWalletTransactions($pdo, $userId, 5);
    sendMessage($chat_id, botFormatTransactionsMessage($transactions), botTransactionsKeyboard());
}


function botTransferServicesMenuButtons(): array
{
    return [
        [
            ['text' => '📂 Transfer Management', 'callback_data' => 'transfer_management_menu']
        ],
        [
            ['text' => '🆘 Support / Help', 'callback_data' => 'transfer_services_support_help']
        ],
        [
            ['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']
        ]
    ];
}

function botTransferManagementMenuButtons(): array
{
    return [
        [
            ['text' => '🌍 Int Transfer Management', 'callback_data' => 'int_transfer_management_menu']
        ],
        [
            ['text' => '🇪🇺 Sepa Transfer Management', 'callback_data' => 'sepa_transfer_management_menu']
        ],
        [
            ['text' => '📲 Wallet Transfer Management', 'callback_data' => 'wallet_transfer_management_menu']
        ],
        [
            ['text' => '↩️ Return to Transfer Services', 'callback_data' => 'transfer_services_menu']
        ]
    ];
}

function botIntTransferManagementMenuButtons(): array
{
    return [
        [
            ['text' => '✅ Start Int Transfer Verification', 'callback_data' => 'int_transfer_start_verification']
        ],
        [
            ['text' => '📊 Verification Status', 'callback_data' => 'int_transfer_verification_status']
        ],
        [
            ['text' => '🆘 Int Transfer Help', 'callback_data' => 'int_transfer_help']
        ],
        [
            ['text' => '↩️ Return to Transfer Management', 'callback_data' => 'transfer_management_menu']
        ]
    ];
}

function botSepaTransferManagementMenuButtons(): array
{
    return [
        [
            ['text' => 'ℹ️ Sepa Transfer Service Notice', 'callback_data' => 'sepa_transfer_service_notice']
        ],
        [
            ['text' => '↩️ Return to Transfer Management', 'callback_data' => 'transfer_management_menu']
        ]
    ];
}

function botWalletTransferManagementMenuButtons(): array
{
    if (function_exists('botWalletTransferModuleButtons')) {
        return botWalletTransferModuleButtons();
    }

    return [
        [
            ['text' => '💳 Make a Wallet Transfer', 'callback_data' => 'wallet_make_transfer']
        ],
        [
            ['text' => '📊 Wallet Transfer Limit Management', 'callback_data' => 'wallet_transfer_limits']
        ],
        [
            ['text' => '📜 Wallet Transfer History', 'callback_data' => 'wallet_transfer_history']
        ],
        [
            ['text' => '🔎 Wallet Transfer Status', 'callback_data' => 'wallet_transfer_status']
        ],
        [
            ['text' => '🆘 Wallet Transfer Support / Help', 'callback_data' => 'wallet_transfer_support_help']
        ],
        [
            ['text' => '↩️ Return to Transfer Management', 'callback_data' => 'transfer_management_menu']
        ]
    ];
}


if (!function_exists('botFeatureControlledUserId')) {
    function botFeatureControlledUserId(array $states, $chat_id): int
    {
        return (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);
    }
}

if (!function_exists('botFeatureRestrictionKeyboard')) {
    function botFeatureRestrictionKeyboard(): array
    {
        return [
            'inline_keyboard' => [
                [
                    ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
                ]
            ]
        ];
    }
}

if (!function_exists('botFeatureAccessCallbackMap')) {
    function botFeatureAccessCallbackMap(): array
    {
        return [
            'transfer_services_menu' => 'transfer_services',
            'latest_wallet_transfers' => 'latest_wallet_transfers',
            'wallet_transfer' => 'wallet_transfer',
            'kyc_verification' => 'kyc_verification',
            'card_order' => 'card_order',
            'vehicle_finance_services' => 'vehicle_finance_services',
            'my_support_tickets' => 'my_support_tickets',
            'my_notifications' => 'my_notifications',
            'secure_direct_communication' => 'secure_direct_communication',
            'secure_direct_view' => 'secure_direct_communication',
            'secure_direct_reply' => 'secure_direct_communication',
            'talk_to_agent' => 'talk_to_agent',
            'wallet_balance' => 'wallet_balance',
            'wallet_transactions' => 'wallet_transactions',
            'account_status' => 'account_status',
        ];
    }
}

if (!function_exists('botHandleFeatureAccessRestriction')) {
    function botHandleFeatureAccessRestriction(int $chat_id, array $states, string $callbackData): bool
    {
        if (!function_exists('bufc_check_callback_access')) {
            return false;
        }

        $map = botFeatureAccessCallbackMap();
        $featureKey = $map[$callbackData] ?? null;
        if (!$featureKey) {
            return false;
        }

        $userId = botFeatureControlledUserId($states, $chat_id);
        if ($userId <= 0) {
            return false;
        }

        $access = bufc_check_callback_access($userId, $featureKey);
        if (!empty($access['allowed'])) {
            return false;
        }

        $message = trim((string)($access['message'] ?? ''));
        if ($message === '') {
            $message = "⚠️ This service is currently not enabled for your account.

Please contact Customer Care for activation assistance.";
        }

        sendMessage($chat_id, $message);
        return true;
    }
}

function getMainMenuButtons($pdo = null, $chat_id = null) {
    $buttons = [
        [
            ['text' => '💸 Transfer Services', 'callback_data' => 'transfer_services_menu']
        ],
        [
            ['text' => '📄 Latest Transfers', 'callback_data' => 'latest_wallet_transfers'],
            ['text' => '💸 Transfer Support', 'callback_data' => 'wallet_transfer']
        ],
        [
            ['text' => '🛡️ KYC & Verification', 'callback_data' => 'kyc_verification'],
            ['text' => '💳 Card Services', 'callback_data' => 'card_order']
        ],
        [
            ['text' => '🚘 Vehicle Finance Services', 'callback_data' => 'vehicle_finance_services']
        ],
        [
            ['text' => '🎫 My Support Tickets', 'callback_data' => 'my_support_tickets'],
            ['text' => '🔔 My Notifications', 'callback_data' => 'my_notifications']
        ],
        [
            ['text' => '🔐 Secure Direct Communication', 'callback_data' => 'secure_direct_communication']
        ],
        [
            ['text' => '👨‍💼 Speak to an Officer', 'callback_data' => 'talk_to_agent']
        ],
        [
            ['text' => '💰 Check Balance', 'callback_data' => 'wallet_balance'],
            ['text' => '📊 My Transactions', 'callback_data' => 'wallet_transactions']
        ],
        [
            ['text' => '📊 Account Review', 'callback_data' => 'account_status'],
            ['text' => '🚪 Secure Exit', 'callback_data' => 'exit_session']
        ]
    ];

    global $states, $chat_id;

    if (function_exists('bufc_filter_menu_buttons') && isset($states) && isset($chat_id)) {
        $userId = botFeatureControlledUserId((array)$states, $chat_id);
        if ($userId > 0) {
            $buttons = bufc_filter_menu_buttons($userId, $buttons, botFeatureAccessCallbackMap());
        }
    }


    // Show receipt request menu only when customer has a pending upload/re-upload request.
    if ($pdo instanceof PDO && $chat_id !== null && function_exists('botVcrHasPendingMenuRequest') && botVcrHasPendingMenuRequest($pdo, $chat_id)) {
        $buttons[] = [
            ['text' => '🧾 Virtual Card Receipt Requests', 'callback_data' => 'vcard_receipts_menu']
        ];
    }

    return $buttons;
}


if (!function_exists('botSecureDirectCommunicationMenuButtons')) {
    function botSecureDirectCommunicationMenuButtons(): array
    {
        return [
            [
                ['text' => '📄 View Active Conversation', 'callback_data' => 'secure_direct_view']
            ],
            [
                ['text' => '✉️ Send Reply', 'callback_data' => 'secure_direct_reply']
            ],
            [
                ['text' => '↩️ Return to Banking Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

if (!function_exists('botSecureDirectCommunicationViewButtons')) {
    function botSecureDirectCommunicationViewButtons(): array
    {
        return [
            [
                ['text' => '✉️ Send Reply', 'callback_data' => 'secure_direct_reply']
            ],
            [
                ['text' => '↩️ Return to Direct Communication', 'callback_data' => 'secure_direct_communication']
            ]
        ];
    }
}

if (!function_exists('botSecureDirectCommunicationReplyButtons')) {
    function botSecureDirectCommunicationReplyButtons(): array
    {
        return [
            [
                ['text' => '↩️ Return to Direct Communication', 'callback_data' => 'secure_direct_communication']
            ]
        ];
    }
}

if (!function_exists('botSecureDirectCommunicationNoThreadButtons')) {
    function botSecureDirectCommunicationNoThreadButtons(): array
    {
        return [
            [
                ['text' => '↩️ Return to Banking Menu', 'callback_data' => 'main_menu']
            ]
        ];
    }
}

if (!function_exists('botSecureDirectCommunicationNoThreadMessage')) {
    function botSecureDirectCommunicationNoThreadMessage(): string
    {
        return "🔐 <b>FinoviaPay Secure Direct Communication</b>\n\n"
            . "There is currently no active direct communication assigned to your account.\n\n"
            . "If an authorised FinoviaPay officer contacts you, the conversation will appear here automatically.";
    }
}

if (!function_exists('botSecureDirectCommunicationSummaryMessage')) {
    function botSecureDirectCommunicationSummaryMessage(PDO $pdo, array $thread): string
    {
        $threadId = htmlspecialchars((string)($thread['thread_id'] ?? 'N/A'), ENT_QUOTES, 'UTF-8');
        $department = htmlspecialchars((string)($thread['department'] ?? 'Not Available'), ENT_QUOTES, 'UTF-8');
        $officerName = htmlspecialchars((string)($thread['officer_name'] ?? 'FinoviaPay Officer'), ENT_QUOTES, 'UTF-8');
        $ticketId = trim((string)($thread['ticket_id'] ?? ''));
        $status = strtolower(trim((string)($thread['status'] ?? 'open'))) === 'closed' ? 'Closed' : 'Open';
        $lastMessageAtRaw = trim((string)($thread['last_message_at'] ?? ''));
        $lastMessageAt = $lastMessageAtRaw !== '' ? date('d M Y H:i', strtotime($lastMessageAtRaw)) : 'Not Available';

        $message = "🔐 <b>FinoviaPay Secure Direct Communication</b>\n\n"
            . "You currently have an active direct communication from an authorised FinoviaPay officer.\n\n"
            . "<b>Reference</b>\n{$threadId}\n\n"
            . "<b>Department</b>\n{$department}\n\n"
            . "<b>Officer</b>\n{$officerName}\n\n";

        if ($ticketId !== '') {
            $message .= "<b>Ticket ID</b>\n" . htmlspecialchars($ticketId, ENT_QUOTES, 'UTF-8') . "\n\n";
        }

        $message .= "<b>Status</b>\n{$status}\n\n"
            . "<b>Last Update</b>\n" . htmlspecialchars($lastMessageAt, ENT_QUOTES, 'UTF-8') . "\n\n";

        $unreadCount = 0;
        if (function_exists('botTelegramDirectCountUnreadCustomerMessages')) {
            $unreadCount = (int)botTelegramDirectCountUnreadCustomerMessages($pdo, (string)($thread['thread_id'] ?? ''));
        }

        if ($unreadCount > 0) {
            $message .= "<b>Unread Officer Messages</b>\n" . (int)$unreadCount . "\n\n";
        }

        $message .= "You may review the conversation history or send your secure reply using the options below.";
        return $message;
    }
}

if (!function_exists('botSecureDirectCommunicationHistoryMessage')) {
    function botSecureDirectCommunicationHistoryMessage(PDO $pdo, array $thread, int $limit = 10): string
    {
        $threadId = (string)($thread['thread_id'] ?? '');
        $messages = function_exists('botTelegramDirectGetMessagesByThreadId')
            ? botTelegramDirectGetMessagesByThreadId($pdo, $threadId, max(1, min($limit, 20)))
            : [];

        $lines = [];
        $lines[] = "📄 <b>Active Direct Communication</b>";
        $lines[] = "";
        $lines[] = "<b>Reference:</b> " . htmlspecialchars($threadId !== '' ? $threadId : 'N/A', ENT_QUOTES, 'UTF-8');

        if (!empty($thread['department'])) {
            $lines[] = "<b>Department:</b> " . htmlspecialchars((string)$thread['department'], ENT_QUOTES, 'UTF-8');
        }
        if (!empty($thread['officer_name'])) {
            $lines[] = "<b>Officer:</b> " . htmlspecialchars((string)$thread['officer_name'], ENT_QUOTES, 'UTF-8');
        }
        if (!empty($thread['ticket_id'])) {
            $lines[] = "<b>Ticket ID:</b> " . htmlspecialchars((string)$thread['ticket_id'], ENT_QUOTES, 'UTF-8');
        }

        $lines[] = "";
        $lines[] = "<b>Recent Messages</b>";

        if (empty($messages)) {
            $lines[] = "No messages are currently available in this communication thread.";
            return implode("\n", $lines);
        }

        $messages = array_slice($messages, -1 * max(1, min($limit, 20)));
        foreach ($messages as $row) {
            $senderType = strtolower((string)($row['sender_type'] ?? 'system'));
            if ($senderType === 'officer') {
                $label = 'Officer';
            } elseif ($senderType === 'customer') {
                $label = 'You';
            } else {
                $label = 'System';
            }

            $createdAtRaw = trim((string)($row['created_at'] ?? ''));
            $createdAt = $createdAtRaw !== '' ? date('d M Y H:i', strtotime($createdAtRaw)) : 'Not Available';
            $messageText = htmlspecialchars((string)($row['message_text'] ?? ''), ENT_QUOTES, 'UTF-8');

            $lines[] = "";
            $lines[] = "• <b>{$label}</b> — " . htmlspecialchars($createdAt, ENT_QUOTES, 'UTF-8');
            $lines[] = $messageText !== '' ? $messageText : 'Message not available.';
        }

        return implode("\n", $lines);
    }
}

function sendVerifiedWelcomeMenu(PDO $pdo, $chat_id, array $user): void {
    $user = function_exists('botResolveKycUserData') ? botResolveKycUserData($pdo, $user) : $user;

    if (function_exists('botUserHasKycMenuAccess') && !botUserHasKycMenuAccess($pdo, $user)) {
        if (function_exists('botSendKycRestrictionScreen')) {
            botSendKycRestrictionScreen($chat_id, $user);
            return;
        }
    }

    $name = htmlspecialchars($user['name'] ?? 'Customer', ENT_QUOTES, 'UTF-8');
    $accountNumber = htmlspecialchars((string)($user['account_number'] ?? 'Not Available Yet'), ENT_QUOTES, 'UTF-8');
    $maskedEmail = htmlspecialchars(botOtpMaskEmail((string)($user['email'] ?? '')), ENT_QUOTES, 'UTF-8');

    $greeting = "🏦 <b>FinoviaPay Digital Banking Services</b>\n\n"
        . "Welcome back to our secure customer banking channel. Your identity and email verification have been completed successfully.\n\n"
        . "👤 <b>Customer Name</b>\n{$name}\n\n"
        . "🛡️ <b>Verification Status</b>\nVerified & Protected\n\n"
        . "📧 <b>Verified Email</b>\n{$maskedEmail}\n\n"
        . "🏷️ <b>Account Reference</b>\n{$accountNumber}\n\n"
        . "Please select one of the professional banking service options below to continue.";

    sendInlineKeyboard($chat_id, $greeting, getMainMenuButtons($pdo, $chat_id));

    if (function_exists('botCheckPendingVirtualCardReceipts')) {
        botCheckPendingVirtualCardReceipts($pdo, $chat_id);
    }

    $userId = (int)($user['id'] ?? 0);
    if ($userId > 0) {
        $pendingKyc = getPendingKycRequestByUserId($pdo, $userId);
        if ($pendingKyc) {
            $department = $pendingKyc['department'] ?? 'Verification Department';
            $itemsText = formatKycRequestedItems($pdo, $pendingKyc);

            sendInlineKeyboard(
                $chat_id,
                "⚠️ FinoviaPay Verification Notice\n\nA verification request is currently pending for your account.\n\nDepartment: {$department}\n\nRequired Verification Items:\n{$itemsText}\n\nPlease complete the requested verification through the secure upload option below.",
                [
                    [
                        ['text' => 'Open Verification Upload', 'callback_data' => 'kyc_open_request']
                    ]
                ]
            );
        }
    }
}



function botBlockedAccountRecoveryDocTypeButtons(): array
{
    return [
        [
            ['text' => 'National ID Card', 'callback_data' => 'blocked_recovery_doc_national_id'],
            ['text' => 'Passport', 'callback_data' => 'blocked_recovery_doc_passport']
        ],
        [
            ['text' => 'Residence Permit', 'callback_data' => 'blocked_recovery_doc_residence_permit']
        ],
        [
            ['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']
        ]
    ];
}

function botTelegramApiRequest(string $method, array $payload = []): ?array
{
    global $token;

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot{$token}/{$method}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    $response = curl_exec($ch);
    curl_close($ch);

    if ($response === false || $response === '') {
        return null;
    }

    $decoded = json_decode($response, true);
    return is_array($decoded) ? $decoded : null;
}

function botBlockedAccountRecoveryDownloadTelegramFile(string $fileId, string $prefix = 'file'): ?array
{
    global $token;

    $meta = botTelegramApiRequest('getFile', ['file_id' => $fileId]);
    if (!is_array($meta) || empty($meta['ok']) || empty($meta['result']['file_path'])) {
        return null;
    }

    $filePath = (string)$meta['result']['file_path'];
    $ext = pathinfo($filePath, PATHINFO_EXTENSION);
    $ext = $ext !== '' ? strtolower($ext) : 'jpg';

    $dir = __DIR__ . '/uploads/blocked_account_recovery';
    if (!is_dir($dir)) {
        @mkdir($dir, 0775, true);
    }
    if (!is_dir($dir) || !is_writable($dir)) {
        return null;
    }

    $safePrefix = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $prefix);
    $filename = $safePrefix . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
    $absolutePath = $dir . '/' . $filename;
    $downloadUrl = "https://api.telegram.org/file/bot{$token}/{$filePath}";
    $binary = @file_get_contents($downloadUrl);

    if ($binary === false) {
        return null;
    }

    if (@file_put_contents($absolutePath, $binary) === false) {
        return null;
    }

    return [
        'absolute_path' => $absolutePath,
        'relative_path' => 'uploads/blocked_account_recovery/' . $filename,
        'filename' => $filename,
        'telegram_file_id' => $fileId,
    ];
}

function botBlockedAccountRecoveryGetBestFileId(array $update): ?string
{
    $message = $update['message'] ?? [];
    if (!empty($message['photo']) && is_array($message['photo'])) {
        $photo = end($message['photo']);
        if (is_array($photo) && !empty($photo['file_id'])) {
            return (string)$photo['file_id'];
        }
    }

    if (!empty($message['document']['file_id'])) {
        return (string)$message['document']['file_id'];
    }

    return null;
}

function botBlockedAccountRecoveryCreateStorage(PDO $pdo): void
{
    try {
        $pdo->exec("CREATE TABLE IF NOT EXISTS blocked_account_recovery_requests (
            id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
            recovery_reference VARCHAR(50) NOT NULL,
            ticket_id VARCHAR(50) DEFAULT NULL,
            telegram_id BIGINT DEFAULT NULL,
            user_id INT DEFAULT NULL,
            customer_name VARCHAR(255) DEFAULT NULL,
            customer_email VARCHAR(255) DEFAULT NULL,
            customer_mobile VARCHAR(50) DEFAULT NULL,
            account_status VARCHAR(50) DEFAULT NULL,
            document_type VARCHAR(100) DEFAULT NULL,
            date_of_birth DATE DEFAULT NULL,
            father_name VARCHAR(255) DEFAULT NULL,
            selfie_path VARCHAR(255) DEFAULT NULL,
            document_front_path VARCHAR(255) DEFAULT NULL,
            document_back_path VARCHAR(255) DEFAULT NULL,
            submitted_payload LONGTEXT DEFAULT NULL,
            status VARCHAR(50) NOT NULL DEFAULT 'pending_review',
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_recovery_reference (recovery_reference),
            KEY idx_telegram_id (telegram_id),
            KEY idx_user_id (user_id),
            KEY idx_ticket_id (ticket_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    } catch (Throwable $e) {
        // Keep flow alive even if table creation fails.
    }
}

function botBlockedAccountRecoveryGenerateReference(): string
{
    return 'FP-BAR-' . strtoupper(substr(bin2hex(random_bytes(5)), 0, 10));
}

function botBlockedAccountRecoveryCreateSupportTicket(PDO $pdo, array $state, int $chatId, string $recoveryReference): string
{
    $ticketId = generateSupportTicketId();
    $user = $state['blocked_account_recovery']['user'] ?? [];
    $docType = (string)($state['blocked_account_recovery']['document_type'] ?? 'Blocked Account Recovery');
    $dob = (string)($state['blocked_account_recovery']['dob'] ?? '');
    $fatherName = (string)($state['blocked_account_recovery']['father_name'] ?? '');
    $selfie = (string)($state['blocked_account_recovery']['selfie_path'] ?? '');
    $front = (string)($state['blocked_account_recovery']['document_front_path'] ?? '');
    $back = (string)($state['blocked_account_recovery']['document_back_path'] ?? '');

    $description = "Blocked account recovery request submitted through Telegram bot.\n"
        . "Recovery Reference: {$recoveryReference}\n"
        . "Document Type: {$docType}\n"
        . "Date of Birth: {$dob}\n"
        . "Father Name: {$fatherName}\n"
        . "Selfie: {$selfie}\n"
        . "Document Front: {$front}\n"
        . "Document Back: {$back}";

    try {
        $stmt = $pdo->prepare("
            INSERT INTO support_tickets
            (ticket_id, user_id, customer_name, telegram_id, category, department, issue_type, description, status, created_at)
            VALUES
            (:ticket_id, :user_id, :customer_name, :telegram_id, :category, :department, :issue_type, :description, :status, NOW())
        ");
        $stmt->execute([
            ':ticket_id' => $ticketId,
            ':user_id' => !empty($user['id']) ? (int)$user['id'] : null,
            ':customer_name' => (string)($user['name'] ?? 'Blocked Account Recovery Customer'),
            ':telegram_id' => $chatId,
            ':category' => 'Blocked Account Recovery',
            ':department' => 'Security & Compliance Department',
            ':issue_type' => 'Blocked Account Review',
            ':description' => $description,
            ':status' => 'open',
        ]);
    } catch (Throwable $e) {
        // Support ticket insertion is best-effort.
    }

    try {
        $stmt = $pdo->prepare("INSERT INTO ticket_messages (ticket_id, sender, message, created_at) VALUES (:ticket_id, 'customer', :message, NOW())");
        $stmt->execute([
            ':ticket_id' => $ticketId,
            ':message' => $description,
        ]);
    } catch (Throwable $e) {
        // Ignore if ticket_messages table differs.
    }

    return $ticketId;
}

function botBlockedAccountRecoveryStoreRequest(PDO $pdo, array $state, int $chatId, string $recoveryReference, string $ticketId): void
{
    botBlockedAccountRecoveryCreateStorage($pdo);

    $user = $state['blocked_account_recovery']['user'] ?? [];
    $payload = $state['blocked_account_recovery'] ?? [];

    try {
        $stmt = $pdo->prepare("
            INSERT INTO blocked_account_recovery_requests
            (recovery_reference, ticket_id, telegram_id, user_id, customer_name, customer_email, customer_mobile, account_status, document_type, date_of_birth, father_name, selfie_path, document_front_path, document_back_path, submitted_payload, status, created_at, updated_at)
            VALUES
            (:recovery_reference, :ticket_id, :telegram_id, :user_id, :customer_name, :customer_email, :customer_mobile, :account_status, :document_type, :date_of_birth, :father_name, :selfie_path, :document_front_path, :document_back_path, :submitted_payload, 'pending_review', NOW(), NOW())
        ");
        $stmt->execute([
            ':recovery_reference' => $recoveryReference,
            ':ticket_id' => $ticketId,
            ':telegram_id' => $chatId,
            ':user_id' => !empty($user['id']) ? (int)$user['id'] : null,
            ':customer_name' => (string)($user['name'] ?? ''),
            ':customer_email' => (string)($user['email'] ?? ''),
            ':customer_mobile' => (string)($user['mobile'] ?? ''),
            ':account_status' => (string)($user['status'] ?? 'blocked'),
            ':document_type' => (string)($payload['document_type'] ?? ''),
            ':date_of_birth' => (string)($payload['dob'] ?? ''),
            ':father_name' => (string)($payload['father_name'] ?? ''),
            ':selfie_path' => (string)($payload['selfie_path'] ?? ''),
            ':document_front_path' => (string)($payload['document_front_path'] ?? ''),
            ':document_back_path' => (string)($payload['document_back_path'] ?? ''),
            ':submitted_payload' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
    } catch (Throwable $e) {
        // Keep Telegram flow alive.
    }
}

function botBlockedAccountRecoverySendConfirmationEmail(array $user, string $ticketId, string $recoveryReference): bool
{
    $to = trim((string)($user['email'] ?? ''));
    if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
        return false;
    }

    $customerName = htmlspecialchars((string)($user['name'] ?? 'Customer'), ENT_QUOTES, 'UTF-8');
    $mobile = htmlspecialchars((string)($user['mobile'] ?? 'Not Available'), ENT_QUOTES, 'UTF-8');
    $subject = 'Blocked Account Recovery Request Received - FinoviaPay';

    $html = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>'
        . $subject
        . '</title></head><body style="margin:0;padding:0;background:#f4f7fb;font-family:Arial,Helvetica,sans-serif;color:#1f2937;">'
        . '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background:#f4f7fb;padding:24px 0;">'
        . '<tr><td align="center"><table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width:680px;background:#ffffff;border-radius:16px;overflow:hidden;">'
        . '<tr><td style="background:#0f172a;color:#ffffff;padding:24px 32px;font-size:24px;font-weight:bold;">FinoviaPay Blocked Account Recovery</td></tr>'
        . '<tr><td style="padding:32px;line-height:1.7;font-size:15px;">'
        . '<p style="margin:0 0 18px;">Dear ' . $customerName . ',</p>'
        . '<p style="margin:0 0 18px;">This is an official confirmation that your blocked account recovery request has been successfully received through the FinoviaPay Telegram Banking channel and securely forwarded to our Security &amp; Compliance Department for manual review.</p>'
        . '<p style="margin:0 0 18px;">Our banking officers will examine the information and verification images submitted with your request, including your identity document details and account ownership information, before issuing the next compliance decision.</p>'
        . '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="margin:20px 0;background:#f8fafc;border:1px solid #e5e7eb;border-radius:12px;">'
        . '<tr><td style="padding:18px 20px;"><strong>Recovery Reference:</strong> ' . htmlspecialchars($recoveryReference, ENT_QUOTES, 'UTF-8') . '<br><strong>Support Ticket ID:</strong> ' . htmlspecialchars($ticketId, ENT_QUOTES, 'UTF-8') . '<br><strong>Registered Mobile:</strong> ' . $mobile . '</td></tr></table>'
        . '<p style="margin:0 0 18px;">Review timeframes may vary depending on security checks, document clarity, and account history. If additional information is required, a FinoviaPay officer may contact you using your registered communication details.</p>'
        . '<p style="margin:0 0 18px;">For your protection, FinoviaPay officers will never request your password, card PIN, or one-time verification codes by email.</p>'
        . '<p style="margin:24px 0 0;">FinoviaPay Security &amp; Compliance Department<br>Worldwide Digital Internet Banking</p>'
        . '</td></tr></table></td></tr></table></body></html>';

    $sent = false;
    try {
        $mailerPath = __DIR__ . '/ebankinguser/lib/mailer.php';
        if (is_file($mailerPath)) {
            require_once $mailerPath;
            if (function_exists('sendMail')) {
                $sent = (bool)@sendMail($to, $subject, $html);
            }
        }
    } catch (Throwable $e) {
        $sent = false;
    }

    if (!$sent) {
        $headers = "MIME-Version: 1.0\r\n";
        $headers .= "Content-type:text/html;charset=UTF-8\r\n";
        $headers .= "From: FinoviaPay <no-reply@finoviapay.com>\r\n";
        $sent = @mail($to, $subject, $html, $headers);
    }

    return (bool)$sent;
}

function botBlockedAccountRecoveryFinalize(PDO $pdo, array &$states, string $stateFile, int $chatId): void
{
    $recoveryReference = botBlockedAccountRecoveryGenerateReference();
    $ticketId = botBlockedAccountRecoveryCreateSupportTicket($pdo, $states[$chatId] ?? [], $chatId, $recoveryReference);
    botBlockedAccountRecoveryStoreRequest($pdo, $states[$chatId] ?? [], $chatId, $recoveryReference, $ticketId);

    $user = $states[$chatId]['blocked_account_recovery']['user'] ?? [];
    $emailSent = botBlockedAccountRecoverySendConfirmationEmail($user, $ticketId, $recoveryReference);

    unset($states[$chatId]['blocked_account_recovery']);
    $states[$chatId]['step'] = isset($states[$chatId]['user']) ? 'verified' : 'choose_customer_type';
    saveStates($states, $stateFile);

    $msg = "✅ <b>Blocked Account Recovery Request Submitted</b>\n\n"
        . "Your blocked account recovery request has been successfully recorded and forwarded to the FinoviaPay Security & Compliance Department for review.\n\n"
        . "<b>Recovery Reference</b>\n{$recoveryReference}\n\n"
        . "<b>Support Ticket ID</b>\n{$ticketId}\n\n"
        . "Our banking officers will now review the mobile number match, identity document details, selfie verification, and supporting images submitted with your request.\n\n";

    if ($emailSent) {
        $msg .= "A confirmation email has also been sent to your registered email address.\n\n";
    } else {
        $msg .= "Your request has been registered successfully. A confirmation email could not be sent at this moment, however your recovery request remains safely recorded for officer review.\n\n";
    }

    $msg .= "Thank you for your patience and cooperation.\n\n— FinoviaPay Security & Compliance Department";

    sendInlineKeyboard($chatId, $msg, [[['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']]]);
}

function botHandleBlockedAccountRecoveryCallback(PDO $pdo, array &$states, string $stateFile, int $chatId, string $callbackData): bool
{
    if ($callbackData === 'blocked_account_recovery_start') {
        $states[$chatId]['step'] = 'blocked_recovery_mobile';
        $states[$chatId]['blocked_account_recovery'] = [
            'active' => true,
            'started_at' => date('Y-m-d H:i:s'),
        ];
        saveStates($states, $stateFile);

        sendMessage(
            $chatId,
            "🛡️ <b>Blocked Account Recovery</b>\n\nTo begin your blocked account recovery review, please enter your registered mobile number exactly as recorded on your FinoviaPay profile."
        );
        return true;
    }

    if (strpos($callbackData, 'blocked_recovery_doc_') === 0) {
        if (($states[$chatId]['step'] ?? '') !== 'blocked_recovery_document_type') {
            return true;
        }

        $docType = str_replace('blocked_recovery_doc_', '', $callbackData);
        $docType = ucwords(str_replace('_', ' ', $docType));
        $states[$chatId]['blocked_account_recovery']['document_type'] = $docType;
        $states[$chatId]['step'] = 'blocked_recovery_dob';
        saveStates($states, $stateFile);

        sendMessage(
            $chatId,
            "Thank you.\n\nPlease enter your date of birth in this format: <b>YYYY-MM-DD</b>."
        );
        return true;
    }

    return false;
}

function botHandleBlockedAccountRecoveryMessage(PDO $pdo, array $update, array &$states, string $stateFile, int $chatId, string $text): bool
{
    $step = (string)($states[$chatId]['step'] ?? '');
    $flow = $states[$chatId]['blocked_account_recovery'] ?? null;
    if (!is_array($flow) || empty($flow['active'])) {
        return false;
    }

    if ($step === 'blocked_recovery_mobile' && trim($text) !== '') {
        $mobile = normalizeMobile($text);
        if ($mobile === '') {
            sendMessage($chatId, 'Please enter a valid mobile number to continue.');
            return true;
        }

        $user = findUserByMobile($pdo, $mobile);
        if (!$user || strtolower(trim((string)($user['status'] ?? 'active'))) !== 'blocked') {
            sendInlineKeyboard(
                $chatId,
                "We could not confirm a blocked FinoviaPay account against the mobile number you entered.\n\nPlease make sure you are entering the registered mobile number exactly as stored on your profile. If your account is blocked and you still need assistance, please contact support from the main menu.",
                [[['text' => '↩️ Return to Main Menu', 'callback_data' => 'main_menu']]]
            );
            return true;
        }

        $states[$chatId]['blocked_account_recovery']['mobile'] = $mobile;
        $states[$chatId]['blocked_account_recovery']['user'] = [
            'id' => $user['id'] ?? null,
            'name' => $user['name'] ?? 'Customer',
            'email' => $user['email'] ?? '',
            'mobile' => $user['mobile'] ?? $mobile,
            'status' => $user['status'] ?? 'blocked',
        ];
        $states[$chatId]['step'] = 'blocked_recovery_document_type';
        saveStates($states, $stateFile);

        sendInlineKeyboard(
            $chatId,
            "Thank you. We have located a FinoviaPay customer record with blocked account status against the submitted mobile number.\n\nPlease choose your identity document type to continue the recovery review.",
            botBlockedAccountRecoveryDocTypeButtons()
        );
        return true;
    }

    if ($step === 'blocked_recovery_dob' && trim($text) !== '') {
        $dob = normalizeDob($text);
        if ($dob === false) {
            sendMessage($chatId, 'Invalid date format. Please enter your date of birth in the format YYYY-MM-DD.');
            return true;
        }

        $states[$chatId]['blocked_account_recovery']['dob'] = $dob;
        $states[$chatId]['step'] = 'blocked_recovery_father_name';
        saveStates($states, $stateFile);

        sendMessage($chatId, "Please enter your father name exactly as recorded on your official documents.");
        return true;
    }

    if ($step === 'blocked_recovery_father_name' && trim($text) !== '') {
        $fatherName = trim($text);
        if ($fatherName === '') {
            sendMessage($chatId, 'Please enter your father name to continue.');
            return true;
        }

        $states[$chatId]['blocked_account_recovery']['father_name'] = $fatherName;
        $states[$chatId]['step'] = 'blocked_recovery_selfie';
        saveStates($states, $stateFile);

        sendMessage(
            $chatId,
            "Please upload a clear selfie image of yourself.\n\nFor faster review, make sure your face is fully visible, the image is well lit, and the picture is not blurred."
        );
        return true;
    }

    if (in_array($step, ['blocked_recovery_selfie', 'blocked_recovery_document_front', 'blocked_recovery_document_back'], true)) {
        $fileId = botBlockedAccountRecoveryGetBestFileId($update);
        if (!$fileId) {
            if (trim($text) !== '') {
                sendMessage($chatId, 'Please upload an image or document file to continue this verification step.');
                return true;
            }
            return false;
        }

        $prefixMap = [
            'blocked_recovery_selfie' => 'selfie',
            'blocked_recovery_document_front' => 'document_front',
            'blocked_recovery_document_back' => 'document_back',
        ];
        $pathKeyMap = [
            'blocked_recovery_selfie' => 'selfie_path',
            'blocked_recovery_document_front' => 'document_front_path',
            'blocked_recovery_document_back' => 'document_back_path',
        ];

        $fileInfo = botBlockedAccountRecoveryDownloadTelegramFile($fileId, $prefixMap[$step] ?? 'file');
        if (!$fileInfo || empty($fileInfo['relative_path'])) {
            sendMessage($chatId, 'We were unable to save the uploaded file. Please try sending the image again.');
            return true;
        }

        $states[$chatId]['blocked_account_recovery'][$pathKeyMap[$step]] = $fileInfo['relative_path'];

        if ($step === 'blocked_recovery_selfie') {
            $states[$chatId]['step'] = 'blocked_recovery_document_front';
            saveStates($states, $stateFile);
            sendMessage($chatId, 'Selfie received successfully.\n\nPlease now upload the <b>front side</b> of your identity document.');
            return true;
        }

        if ($step === 'blocked_recovery_document_front') {
            $states[$chatId]['step'] = 'blocked_recovery_document_back';
            saveStates($states, $stateFile);
            sendMessage($chatId, 'Document front side received successfully.\n\nPlease now upload the <b>back side</b> of your identity document.');
            return true;
        }

        if ($step === 'blocked_recovery_document_back') {
            saveStates($states, $stateFile);
            botBlockedAccountRecoveryFinalize($pdo, $states, $stateFile, $chatId);
            return true;
        }
    }

    return false;
}


function findUserByMobileDob($pdo, $mobile, $dob) {
    $sql = "
        SELECT *
        FROM users
        WHERE mobile = :mobile
          AND dob = :dob
        ORDER BY
            CASE WHEN LOWER(COALESCE(status, 'active')) = 'blocked' THEN 0 ELSE 1 END ASC,
            id DESC
        LIMIT 1
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':mobile' => $mobile,
        ':dob' => $dob
    ]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

function findBlockedUserByMobileDob($pdo, $mobile, $dob) {
    $sql = "
        SELECT *
        FROM users
        WHERE mobile = :mobile
          AND dob = :dob
          AND LOWER(TRIM(COALESCE(status, 'active'))) = 'blocked'
        ORDER BY id DESC
        LIMIT 1
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':mobile' => $mobile,
        ':dob' => $dob
    ]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

function debugUserMatchesByMobileDob($pdo, $mobile, $dob) {
    try {
        $stmt = $pdo->prepare("
            SELECT id, name, mobile, dob, status, block_reason_category, account_number
            FROM users
            WHERE mobile = :mobile AND dob = :dob
            ORDER BY id DESC
        ");
        $stmt->execute([
            ':mobile' => $mobile,
            ':dob' => $dob
        ]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) {
        return [];
    }
}


function findUserByMobile($pdo, $mobile) {
    $sql = "
        SELECT *
        FROM users
        WHERE mobile = :mobile
        ORDER BY
            CASE WHEN LOWER(COALESCE(status, 'active')) = 'blocked' THEN 0 ELSE 1 END ASC,
            id DESC
        LIMIT 1
    ";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([
        ':mobile' => $mobile
    ]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}


function findUserByTelegramId($pdo, $telegramId) {
    try {
        $sql = "SELECT * FROM users WHERE telegram_id = :telegram_id LIMIT 1";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([
            ':telegram_id' => (string)$telegramId
        ]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Throwable $e) {
        return false;
    }
}

function getBlockedUserRecord($pdo, $chat_id, $states) {
    try {
        $stateUser = $states[$chat_id]['user'] ?? $states[$chat_id]['verified_user'] ?? $states[$chat_id]['customer'] ?? null;

        if (is_array($stateUser) && !empty($stateUser['id'])) {
            $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 1");
            $stmt->execute([':id' => (int)$stateUser['id']]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($user && trim(strtolower((string)($user['status'] ?? 'active'))) === 'blocked') {
                return $user;
            }
        }

        if (is_array($stateUser) && !empty($stateUser['mobile']) && !empty($stateUser['dob'])) {
            $stmt = $pdo->prepare("SELECT * FROM users WHERE mobile = :mobile AND dob = :dob LIMIT 1");
            $stmt->execute([
                ':mobile' => trim((string)$stateUser['mobile']),
                ':dob' => trim((string)$stateUser['dob'])
            ]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($user && trim(strtolower((string)($user['status'] ?? 'active'))) === 'blocked') {
                return $user;
            }
        }
    } catch (Throwable $e) {
        return false;
    }

    return false;
}

function sendBlockedAccountNotice($pdo, $chat_id, $states) {
    $blockedUser = getBlockedUserRecord($pdo, $chat_id, $states);

    $message  = "🚫 <b>Account Access Restricted</b>

";
    $message .= "Dear Customer,

";
    $message .= "Your FinoviaPay digital banking account is currently restricted due to security or compliance related reasons.

";
    $message .= "As a result, access to automated banking services within this support assistant has been temporarily limited.

";
    $message .= "This restriction may be related to one or more of the following:
";
    $message .= "• Security verification requirements
";
    $message .= "• Compliance review
";
    $message .= "• Unresolved account verification matters
";
    $message .= "• Suspicious activity monitoring

";
    $message .= "For security and privacy reasons, detailed account information cannot be disclosed through automated channels.

";

    if ($blockedUser && !empty($blockedUser['block_reason_category'])) {
        $message .= "<b>Restriction Category</b>
" . htmlspecialchars((string)$blockedUser['block_reason_category'], ENT_QUOTES, 'UTF-8') . "

";
    }

    if ($blockedUser && !empty($blockedUser['block_reason_text'])) {
        $message .= "<b>Support Note</b>
" . htmlspecialchars((string)$blockedUser['block_reason_text'], ENT_QUOTES, 'UTF-8') . "

";
    }

    $message .= "If you believe this restriction has been applied in error or you would like to request a review of your account status, please contact our Customer Support Services through the secure support ticket option below.

";
    $message .= "Our banking operations team will carefully review your request and provide further guidance.

";
    $message .= "Thank you for your patience and cooperation.

";
    $message .= "— FinoviaPay Security & Compliance Department";

    $buttons = [
        [
            ['text' => '📩 Submit Recovery Request', 'callback_data' => 'blocked_account_recovery']
        ]
    ];

    sendInlineKeyboard($chat_id, $message, $buttons);
}


function closeCustomerSession($pdo, $telegram_id, &$states, $state_file) {
    $openChat = getOpenSupportChat($pdo, $telegram_id);

    if ($openChat) {
        $stmt = $pdo->prepare("UPDATE support_chats SET status = 'closed', updated_at = NOW() WHERE id = :id");
        $stmt->execute([':id' => $openChat['id']]);
        insertSupportMessage($pdo, $openChat['id'], 'system', 'Customer exited the chat session and was logged out.');
    }

    unset($states[$telegram_id]);
    saveStates($states, $state_file);

    $exitMsg = "You have now exited your Finoviapay Customer Support session and have been securely logged out.\n\nTo speak with us again at any time, simply enter /start and you may continue using our Telegram digital banking support services whenever required.\n\nThank you for choosing Finoviapay.";
    sendMessage($telegram_id, $exitMsg);
}

function getOpenSupportChat($pdo, $telegram_id) {
    $stmt = $pdo->prepare("SELECT * FROM support_chats WHERE telegram_id = :tid AND status IN ('waiting','active','transferred') ORDER BY id DESC LIMIT 1");
    $stmt->execute([':tid' => $telegram_id]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

function getLatestSupportChatAnyStatus($pdo, $telegram_id) {
    $stmt = $pdo->prepare("SELECT * FROM support_chats WHERE telegram_id = :tid ORDER BY id DESC LIMIT 1");
    $stmt->execute([':tid' => $telegram_id]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}

function createSupportChat($pdo, $telegram_id, $user = null) {
    $customer_name   = $user['name'] ?? null;
    $customer_mobile = $user['mobile'] ?? null;
    $customer_dob    = $user['dob'] ?? null;
    $customer_email  = $user['email'] ?? null;
    $user_id         = $user['id'] ?? null;
    $ticket_id       = generateTicketId();
    $queue_position  = getQueuePosition($pdo);
    $estimated_wait_minutes = estimateWaitMinutes($queue_position);

    $stmt = $pdo->prepare("
        INSERT INTO support_chats
        (ticket_id, telegram_id, user_id, customer_name, customer_mobile, customer_dob, customer_email, queue_position, estimated_wait_minutes, status, created_at, updated_at)
        VALUES
        (:ticket_id, :telegram_id, :user_id, :customer_name, :customer_mobile, :customer_dob, :customer_email, :queue_position, :estimated_wait_minutes, 'waiting', NOW(), NOW())
    ");

    $stmt->execute([
        ':ticket_id' => $ticket_id,
        ':telegram_id' => $telegram_id,
        ':user_id' => $user_id,
        ':customer_name' => $customer_name,
        ':customer_mobile' => $customer_mobile,
        ':customer_dob' => $customer_dob,
        ':customer_email' => $customer_email,
        ':queue_position' => $queue_position,
        ':estimated_wait_minutes' => $estimated_wait_minutes
    ]);

    return [
        'id' => $pdo->lastInsertId(),
        'ticket_id' => $ticket_id,
        'queue_position' => $queue_position,
        'estimated_wait_minutes' => $estimated_wait_minutes
    ];
}

function insertSupportMessage($pdo, $chat_id, $sender, $message) {
    $stmt = $pdo->prepare("
        INSERT INTO support_messages (chat_id, sender, message, created_at)
        VALUES (:chat_id, :sender, :message, NOW())
    ");
    $stmt->execute([
        ':chat_id' => $chat_id,
        ':sender' => $sender,
        ':message' => $message
    ]);
}

function notifyChatLifecycleUpdates($pdo, $telegram_id) {
    $chat = getLatestSupportChatAnyStatus($pdo, $telegram_id);
    if (!$chat) {
        return;
    }

    if ((int)($chat['agent_joined'] ?? 0) === 1 && (int)($chat['agent_joined_notified'] ?? 0) === 0) {
        $officerName = trim($chat['agent_name'] ?? 'FinoviaPay Support Officer');
        $officerId   = trim($chat['banker_officer_id'] ?? 'Not Available');
        $department  = trim($chat['department'] ?? 'Customer Support Department');

        $joinMsg = "Your live support request has now been accepted and joined by a FinoviaPay Support Officer.\n\nAssigned Support Officer: {$officerName}\nBanker Officer ID: {$officerId}\nDepartment: {$department}\n\nYour assigned officer is now reviewing your request and will continue assisting you through this secure conversation.\n\nFor your protection, please do not share your account password, card PIN, or one-time verification codes during this chat unless specifically instructed through an official and secure FinoviaPay verification process.\n\nYou may now continue your conversation with the assigned support officer.";

        sendMessage($telegram_id, $joinMsg);

        $upd = $pdo->prepare("UPDATE support_chats SET agent_joined_notified = 1, updated_at = NOW() WHERE id = :id");
        $upd->execute([':id' => $chat['id']]);

        insertSupportMessage($pdo, $chat['id'], 'system', 'Customer was notified that a support officer joined the conversation.');
    }

    if (!empty($chat['transferred_to_agent']) && (int)($chat['transfer_notified'] ?? 0) === 0) {
        $previousOfficer = trim($chat['agent_name'] ?? 'FinoviaPay Support Officer');
        $previousOfficerId = trim($chat['banker_officer_id'] ?? 'Not Available');
        $newDepartment = trim($chat['department'] ?? 'Customer Support Department');

        $transferMsg = "Your live support request is currently being transferred for further review and assistance.\n\nYour conversation was previously being handled by:\n\nSupport Officer: {$previousOfficer}\nBanker Officer ID: {$previousOfficerId}\n\nIn order to provide you with the most appropriate assistance, your request is now being redirected to another FinoviaPay Support Officer and/or the relevant department.\n\nNew Handling Department: {$newDepartment}\n\nPlease remain connected while we complete the transfer process. You will receive another confirmation message as soon as the newly assigned support officer joins your conversation.\n\nThank you for your patience and understanding.";

        sendMessage($telegram_id, $transferMsg);

        $upd = $pdo->prepare("UPDATE support_chats SET transfer_notified = 1, updated_at = NOW() WHERE id = :id");
        $upd->execute([':id' => $chat['id']]);

        insertSupportMessage($pdo, $chat['id'], 'system', 'Customer was notified that the chat was transferred.');
    }

    if ($chat['status'] === 'closed' && (int)($chat['closed_notified'] ?? 0) === 0) {
        $officerName = trim($chat['agent_name'] ?? 'FinoviaPay Support Officer');
        $officerId   = trim($chat['banker_officer_id'] ?? 'Not Available');
        $department  = trim($chat['department'] ?? 'Customer Support Department');

        $closeMsg = "Your live support session with FinoviaPay Customer Support has now been completed and formally closed.\n\nThis support conversation was handled by:\n\nSupport Officer: {$officerName}\nBanker Officer ID: {$officerId}\nDepartment: {$department}\n\nAccording to the assigned support officer, your request has been reviewed and the chat session has now been concluded.\n\nIf you require any further assistance regarding the same matter or a new request, you may contact FinoviaPay Customer Support again through the official support channel at any time.\n\nThank you for choosing FinoviaPay and for banking with us.\n\nPlease rate your support experience and, if required, request a chat transcript below.";

        $ratingButtons = [
            [
                ['text' => '⭐ 1', 'callback_data' => 'rate_chat_1'],
                ['text' => '⭐ 2', 'callback_data' => 'rate_chat_2'],
                ['text' => '⭐ 3', 'callback_data' => 'rate_chat_3']
            ],
            [
                ['text' => '⭐ 4', 'callback_data' => 'rate_chat_4'],
                ['text' => '⭐ 5', 'callback_data' => 'rate_chat_5']
            ],
            [
                ['text' => 'Send Chat Transcript', 'callback_data' => 'send_transcript']
            ]
        ];

        sendMessage($telegram_id, $closeMsg, ['inline_keyboard' => $ratingButtons]);

        $upd = $pdo->prepare("UPDATE support_chats SET closed_notified = 1, rating_requested = 1, updated_at = NOW() WHERE id = :id");
        $upd->execute([':id' => $chat['id']]);

        insertSupportMessage($pdo, $chat['id'], 'system', 'Customer was notified that the chat session was closed.');
    }
}

$message      = $update['message'] ?? null;
$callback     = $update['callback_query'] ?? null;
$chat_id      = null;
$text         = null;
$callbackData = null;
$callbackId   = null;
$telegram_user_id = null;
$photo = [];
$document = null;

if ($message) {
    $photo = $message['photo'] ?? [];
    $document = $message['document'] ?? null;
    $chat_id = $message['chat']['id'] ?? null;
    $text    = trim($message['text'] ?? '');
    $telegram_user_id = $message['from']['id'] ?? null;
}
if ($callback) {
    $chat_id      = $callback['message']['chat']['id'] ?? null;
    $callbackData = $callback['data'] ?? null;
    $callbackId   = $callback['id'] ?? null;
    $telegram_user_id = $callback['from']['id'] ?? $telegram_user_id;
}

if (!$chat_id) {
    exit;
}

// ===== VIRTUAL CARD RECEIPT REQUEST / UPLOAD FLOW =====
if (($callback || $message) && function_exists('botVirtualCardReceiptsHandle')) {
    if (botVirtualCardReceiptsHandle($pdo, $chat_id, $message, $callbackData ? (string)$callbackData : null, $callbackId)) {
        exit;
    }
}

if ($callback && !empty($callbackData) && botHandleGeneralNotificationConfirmation($pdo, (string)$callbackData, $callbackId, $chat_id)) {
    exit;
}

if ($callback && !empty($callbackData) && botVirtualCardNotificationHandleCallback($pdo, (string)$callbackData, $callbackId, $chat_id)) {
    exit;
}

if ($callback && !empty($callbackData) && strpos((string)$callbackData, 'pollvote_') === 0) {
    $pollVoteResult = bfp_handle_vote($chat_id, (string)$callbackData);
    if ($callbackId) {
        answerCallbackQuery($callbackId, 'Feedback processed');
    }

    if (!empty($pollVoteResult['message'])) {
        sendMessage($chat_id, (string)$pollVoteResult['message']);
    }

    if (!empty($pollVoteResult['result_message'])) {
        sendMessage($chat_id, (string)$pollVoteResult['result_message']);
    }

    if (!empty($pollVoteResult['comment_message'])) {
        sendMessage($chat_id, (string)$pollVoteResult['comment_message']);
    }

    if (empty($pollVoteResult['message']) && empty($pollVoteResult['result_message']) && empty($pollVoteResult['comment_message'])) {
        sendMessage($chat_id, 'Your feedback could not be processed.');
    }

    exit;
}


if ($message && $text === '/start') {
    botLogJoinStart($pdo, $update);
}

if ($message && $text !== '' && function_exists('bfp_handle_comment_message')) {
    $pollCommentFlow = bfp_handle_comment_message($chat_id, $text);
    if (!empty($pollCommentFlow['handled'])) {
        sendMessage($chat_id, (string)($pollCommentFlow['message'] ?? 'Thank you.'));
        exit;
    }
}

// Always honor core commands before onboarding upload handlers.
if ($message && ($text === '/start' || $text === '/exit')) {
    if ($text === '/exit') {
        closeCustomerSession($pdo, $chat_id, $states, $state_file);
        exit;
    }

    $states[$chat_id] = ['step' => 'choose_customer_type'];
    saveStates($states, $state_file);
    sendInlineKeyboard($chat_id, botWelcomeMessage(), botMainEntryButtons());
    exit;
}

if (botHandleAccountOpeningReviewUploadCompat($pdo, $states, $state_file, $update, $chat_id)) {
    exit;
}

if (botHandleNewAccountUploadStep($pdo, $update, $states, $state_file, $chat_id)) {
    exit;
}

// IMPORTANT: Handle International Transfer verification uploads before generic
// digital service document upload handlers, otherwise ID photos can be captured
// by the wrong feature flow.
if (botHandleIntTransferUploads($states, $state_file, $update, $chat_id)) {
    exit;
}

// Handle Phase 2 KYC verification uploads before the generic digital-service
// upload flow. This Phase 2 flow is fully separate from the older
// kyc_verification.php feature.
if (
    (($callbackData ?? '') === 'kyc_phase2_open_request') ||
    (($callbackData ?? '') === 'start_kyc_phase2_verification') ||
    (($states[$chat_id]['kyc_phase2_verification']['active'] ?? false) === true)
) {
    require __DIR__ . '/kyc_phase2_verification_flow.php';
}

if (botHandleDigitalServiceDocumentUpload($pdo, $update, $states, $state_file)) {
    exit;
}

try {
    notifyChatLifecycleUpdates($pdo, $chat_id);

    $state = $states[$chat_id] ?? null;

    $publicSteps = ['choose_customer_type', 'await_mobile', 'await_dob', 'awaiting_email_otp'];
    $currentStep = (string)($states[$chat_id]['step'] ?? '');
    $isNewAccountStep = ($currentStep === 'new_customer_menu' || $currentStep === 'new_customer_open_account' || strpos($currentStep, 'new_account_') === 0);
    $sessionTimeoutMode = botGetSetting($pdo, 'telegram_bot_session_timeout_mode', 'enabled');

    if ($sessionTimeoutMode === 'enabled') {
        if ($text !== '/start' && !$isNewAccountStep && !in_array($currentStep, $publicSteps, true) && botIsSecureSessionExpired($states, $chat_id)) {
            if (isset($states[$chat_id]) && is_array($states[$chat_id])) {
                unset($states[$chat_id]['email_otp']);
                $states[$chat_id]['step'] = 'choose_customer_type';
                saveStates($states, $state_file);
            }

            $expiredMessage = botHandleExpiredSecureSession($states, $state_file, $chat_id);
            sendMessage($chat_id, $expiredMessage);

            sendInlineKeyboard($chat_id, "Please begin the secure identification process again.", botMainEntryButtons());
            exit;
        }

        if (botHasValidSecureSession($states, $chat_id) && ($text !== '' || !empty($callbackData))) {
            botRefreshSecureSession($states, $state_file, $chat_id);
            $state = $states[$chat_id] ?? null;
        }
    }
    if ($text === '/start') {
        $states[$chat_id] = ['step' => 'choose_customer_type'];
        saveStates($states, $state_file);
        sendInlineKeyboard($chat_id, botWelcomeMessage(), botMainEntryButtons());
        exit;
    }


    // Priority route for separate Virtual Card module
    if (
        $callbackData === 'ticket_card_type_virtual' ||
        strpos((string)$callbackData, 'virtual_card_') === 0 ||
        in_array(($states[$chat_id]['step'] ?? ''), [
            'virtual_card_menu',
            'awaiting_eu_mobile',
            'awaiting_vc_eu_mobile',
            'awaiting_vc_weekly_usage',
            'awaiting_vc_average_amount',
            'awaiting_vc_usage_region',
            'awaiting_vc_currency',
            'awaiting_vc_custom_currency'
        ], true)
    ) {
        require __DIR__ . '/virtual_card_services.php';
        exit;
    }

    if ($callbackData === 'contact_customer_service') {
        $activeStep = (string)($states[$chat_id]['step'] ?? '');
        $callbackData = ($activeStep === 'digital_banking_service_disabled' || $activeStep === 'mobile_not_found')
            ? 'talk_to_agent_unverified'
            : 'talk_to_agent';
    }

    if ($callbackData === 'open_digital_banking_ticket') {
        $callbackData = 'start_digital_service_request';
    }
    if ($callbackData === 'request_telegram_rebind') {
        botStartTelegramRebindRequest($states, $state_file, $chat_id);
        sendMessage($chat_id, "📩 <b>Telegram Account Re-Bind Request</b>\n\nPlease enter your full name as registered with FinoviaPay.");
        exit;
    }

    $digitalServiceUserContext = [];
    if (isset($states[$chat_id]['user']) && is_array($states[$chat_id]['user'])) {
        $digitalServiceUserContext = $states[$chat_id]['user'];
    }
    if (!isset($digitalServiceUserContext['id']) && isset($states[$chat_id]['identified_user_id'])) {
        $digitalServiceUserContext['id'] = (int)$states[$chat_id]['identified_user_id'];
    }
    $digitalServiceUserContext['telegram_user_id'] = $telegram_user_id;

    $digitalServiceCallbackResponse = botHandleDigitalServiceCallback($pdo, $states, $state_file, $chat_id, (string)$callbackData, $digitalServiceUserContext);
    if (is_array($digitalServiceCallbackResponse)) {
        if ($callbackId) {
            answerCallbackQuery($callbackId);
        }
        sendInlineKeyboard(
            $chat_id,
            (string)($digitalServiceCallbackResponse['text'] ?? ''),
            $digitalServiceCallbackResponse['buttons'] ?? []
        );
        exit;
    }

    /* ===============================
       PHASE 2 KYC VERIFICATION MODULE
       Fully separate from old kyc_verification.php
    ================================ */

    if (
        ($callbackData ?? '') === 'start_kyc_phase2_verification' ||
        ($callbackData ?? '') === 'kyc_phase2_pending_status' ||
        ($callbackData ?? '') === 'kyc_phase2_open_request' ||
        ($text ?? '') === '✅ Start KYC Verification' ||
        (($states[$chat_id]['kyc_phase2_verification']['active'] ?? false) === true)
    ) {
        if ($callbackId) {
            answerCallbackQuery($callbackId);
        }
        require __DIR__ . '/kyc_phase2_verification_flow.php';
        exit;
    }

    if ($text === '/exit' || $callbackData === 'exit_session') {
        if ($callbackId) {
            answerCallbackQuery($callbackId);
        }
        closeCustomerSession($pdo, $chat_id, $states, $state_file);
        exit;
    }
    if ($text === '/start') {
        $states[$chat_id] = ['step' => 'choose_customer_type'];
        saveStates($states, $state_file);
        sendInlineKeyboard($chat_id, botWelcomeMessage(), botMainEntryButtons());
        exit;
    }


    /* ===============================
       LATEST WALLET TRANSFERS MODULE LINK
       =============================== */
    if (
        ($callbackData ?? '') === 'latest_wallet_transfers' ||
        str_starts_with((string)($callbackData ?? ''), 'lwt_') ||
        (($states[$chat_id]['latest_wallet_transfers']['active'] ?? false) === true)
    ) {
        require __DIR__ . '/latest_wallet_transfers.php';
        exit;
    }


    /* ===============================
       WALLET TRANSFER VERIFICATION UPLOAD MODULE
       =============================== */
    if (
        str_starts_with((string)($callbackData ?? ''), 'wtvr_upload_') ||
        (($states[$chat_id]['wallet_transfer_verification_request']['step'] ?? '') === 'await_upload')
    ) {
        require __DIR__ . '/wallet_transfer_verification_request.php';
        exit;
    }

    /* ===============================
       BLOCKED ACCOUNT SUPPORT MODULE
       =============================== */
    if (
        ($callbackData ?? '') === 'blocked_account_support' ||
        (($states[$chat_id]['blocked_account_ticket']['active'] ?? false) === true)
    ) {
        $states[$chat_id]['step'] = 'blocked_recovery_mobile';
        $states[$chat_id]['blocked_account_recovery'] = [
            'active' => true,
            'started_at' => date('Y-m-d H:i:s'),
        ];
        saveStates($states, $state_file);
        sendMessage($chat_id, "🛡️ <b>Blocked Account Recovery</b>\n\nTo begin your blocked account recovery review, please enter your registered mobile number exactly as recorded on your FinoviaPay profile.");
        exit;
    }

    /* ===============================
       WALLET TRANSFER MODULE LINK
       =============================== */
    if (
        ($callbackData ?? '') === 'wallet_transfer' ||
        str_starts_with((string)($callbackData ?? ''), 'wt_') ||
        (($states[$chat_id]['wallet_transfer']['active'] ?? false) === true)
    ) {
        require __DIR__ . '/wallet_transfer.php';
        exit;
    }

    if ($callbackData === 'main_menu') {
        $state = $states[$chat_id] ?? [];
        $currentStep = (string)($state['step'] ?? '');

        if ($currentStep === 'digital_banking_service_disabled') {
            unset($states[$chat_id]);
            saveStates($states, $state_file);

            $welcome = "Welcome to <b>Finoviapay Customer Support</b>

Welcome to the official FinoviaPay Digital Banking Support channel. This secure automated assistant allows verified customers to access support services, review transfer activity, submit verification documents, and connect with the relevant banking operations team.

<b>Service Notice</b>
FinoviaPay continuously expands its digital banking services. Certain modules are currently under development and will be introduced progressively as they become available.

<b>Currently Available Services</b>
• Latest Wallet Transfer Review
• Wallet Transfer Support
• KYC Identity Verification
• Card Order Support
• Account Status
• Direct Support Agent Access

<b>Upcoming Banking Features</b>
• New Account Opening
• Virtual Card Creation
• Direct Wallet Transfer Initiation
• Additional Digital Banking Services
• Much More";

            sendInlineKeyboard($chat_id, $welcome, botMainEntryButtons());
            exit;
        }

        if (($currentStep === 'verified' || strpos($currentStep, 'secure_direct_communication') === 0) && isset($state['user']) && is_array($state['user'])) {
            sendVerifiedWelcomeMenu($pdo, $chat_id, $state['user']);
        } else {
            $welcome = "🏛️ <b>Welcome to FinoviaPay Premium Digital Banking</b>

Please select one of the premium banking options below to continue securely.";
            sendInlineKeyboard($chat_id, $welcome, botMainEntryButtons());
        }
        exit;
    }

    /* ===============================
       SEPARATE BLOCKED ACCOUNT RECOVERY MODULE
       =============================== */
    if (
        ($callbackData ?? '') === 'blocked_account_recovery' ||
        ($callbackData ?? '') === 'bar_cancel' ||
        str_starts_with((string)($callbackData ?? ''), 'bar_doc_') ||
        (($states[$chat_id]['blocked_account_recovery']['active'] ?? false) === true)
    ) {
        require __DIR__ . '/blocked_account_recovery.php';
    }

    if ($callback) {
        answerCallbackQuery($callbackId);

        if (botHandleBlockedAccountRecoveryCallback($pdo, $states, $state_file, $chat_id, (string)$callbackData)) {
            exit;
        }

        if ($callbackData === 'resend_email_otp') {
            $otpMode = botGetSetting($pdo, 'telegram_bot_email_otp_mode', 'enabled');

            if ($otpMode === 'disabled') {
                sendMessage($chat_id, "Email OTP verification is currently disabled. Please continue using the available FinoviaPay services.");
                if (isset($states[$chat_id]['user']) && is_array($states[$chat_id]['user'])) {
                    sendVerifiedWelcomeMenu($pdo, $chat_id, $states[$chat_id]['user']);
                }
                exit;
            }

            $resend = resendBotEmailOtp($pdo, $states, $state_file, $chat_id);
            sendMessage($chat_id, $resend['message'], botEmailOtpKeyboard());
            exit;
        }


        if (botHandleAccountOpeningReviewCallbackCompat($pdo, $states, $state_file, $chat_id, (string)$callbackData, $telegram_user_id, $callback, $message)) {
            exit;
        }

        if (botHandleNewAccountCallback($pdo, $states, $state_file, $chat_id, (string)$callbackData, $telegram_user_id, $callback, $message)) {
            exit;
        }

        if ($callbackData === 'existing_customer_menu') {
            $callbackData = 'already_customer';
        }

        if (function_exists('botHandleKycMenuRestriction') && botHandleKycMenuRestriction($pdo, $chat_id, $states, (string)$callbackData)) {
            exit;
        }

        if (botHandleFeatureAccessRestriction((int)$chat_id, $states, (string)$callbackData)) {
            exit;
        }

        if ($callbackData === 'new_customer_menu') {
            $states[$chat_id]['step'] = 'new_customer_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "✨ <b>FinoviaPay New Customer Onboarding</b>

Welcome to the premium onboarding section. Please choose an option below to begin your account opening enquiry, review your registration status, or speak with our banking support team.",
                botNewCustomerMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'support_help_menu') {
            $states[$chat_id]['step'] = 'support_help_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🎖️ <b>FinoviaPay Premium Support Centre</b>

Please choose your preferred service channel below for banking assistance, priority guidance, or support ticket submission.",
                botSupportHelpMenuButtons()
            );
            exit;
        }

        if (function_exists('botHandleVehicleFinanceCallback') && botHandleVehicleFinanceCallback($pdo, $states, $state_file, $chat_id, (string)$callbackData)) {
            exit;
        }

        if (botHandleIntTransferCallback($pdo, $states, $state_file, $chat_id, (string)$callbackData)) {
            exit;
        }

        if ($callbackData === 'transfer_services_menu') {
            $states[$chat_id]['step'] = 'transfer_services_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "💸 <b>FinoviaPay Transfer Services</b>

Welcome to FinoviaPay Transfer Services.

Please select one of the available transfer management options below.",
                botTransferServicesMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'transfer_management_menu') {
            $states[$chat_id]['step'] = 'transfer_management_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "📂 <b>Transfer Management</b>

Please select the transfer service you would like to manage.

Available categories include International Transfers, SEPA Transfers, and Wallet Transfers.",
                botTransferManagementMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'int_transfer_management_menu') {
            $states[$chat_id]['step'] = 'int_transfer_management_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🌍 <b>International Transfer Management</b>

International Transfer services are not enabled by default.

To activate this service, you are required to complete a verification process, including basic AML information, identity document verification, and short video confirmation.

Please choose an option below.",
                botIntTransferManagementMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'int_transfer_start_verification') {
            $states[$chat_id]['step'] = 'int_transfer_verification_intro';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🌍 <b>International Transfer Service Activation</b>

Dear Customer,

Thank you for your interest in activating International Transfer services with FinoviaPay.

For your security and to comply with international regulatory requirements, this service is not enabled by default.

Before activation, all customers are required to complete a mandatory verification process, which includes:

• Basic AML (Anti-Money Laundering) information
• Identity document verification
• Short video identity confirmation

This process helps us ensure a secure and compliant banking environment for all customers.

⏱ Estimated completion time: 2–3 minutes
🕒 Review time: 24–72 hours

Please proceed to begin your verification.

— FinoviaPay Compliance Department
Worldwide Digital Internet Banking",
                [
                    [
                        ['text' => '✅ Continue Verification', 'callback_data' => 'int_transfer_verification_continue']
                    ],
                    [
                        ['text' => '↩️ Return to Int Transfer Menu', 'callback_data' => 'int_transfer_management_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'int_transfer_verification_continue') {
            $states[$chat_id]['step'] = 'int_transfer_verification_ready';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "📋 <b>AML Information Requirement</b>

Dear Customer,

As part of our compliance obligations, we are required to collect certain basic information regarding your profile and intended use of International Transfer services.

This information is strictly confidential and will be used only for regulatory and security purposes.

Kindly provide accurate and complete information in the following steps.

— FinoviaPay Compliance Team

<b>Service Status</b>
The full verification workflow will now be connected in the next implementation step.",
                [
                    [
                        ['text' => '📊 Verification Status', 'callback_data' => 'int_transfer_verification_status']
                    ],
                    [
                        ['text' => '↩️ Return to Int Transfer Menu', 'callback_data' => 'int_transfer_management_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'int_transfer_verification_status') {
            $states[$chat_id]['step'] = 'int_transfer_verification_status';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "📊 <b>Verification Status</b>

No International Transfer verification request has been submitted yet.

Please start your verification to proceed.",
                [
                    [
                        ['text' => '✅ Start Int Transfer Verification', 'callback_data' => 'int_transfer_start_verification']
                    ],
                    [
                        ['text' => '↩️ Return to Int Transfer Menu', 'callback_data' => 'int_transfer_management_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'int_transfer_help') {
            $states[$chat_id]['step'] = 'int_transfer_help';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🆘 <b>International Transfer Help</b>

To activate International Transfer services, you must first complete the required compliance verification.

This includes:
• Basic AML information
• Identity document verification
• Short video confirmation

Please use the Start Verification option to begin.",
                [
                    [
                        ['text' => '✅ Start Int Transfer Verification', 'callback_data' => 'int_transfer_start_verification']
                    ],
                    [
                        ['text' => '↩️ Return to Int Transfer Menu', 'callback_data' => 'int_transfer_management_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'sepa_transfer_management_menu') {
            $states[$chat_id]['step'] = 'sepa_transfer_management_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🇪🇺 <b>SEPA Transfer Management</b>

SEPA Transfer service structure has been added successfully. The operational flow will be connected in the next implementation step.",
                botSepaTransferManagementMenuButtons()
            );
            exit;
        }

        if (function_exists('botHandleWalletTransferCallback') && botHandleWalletTransferCallback($chat_id, botFeatureControlledUserId($states, $chat_id), (string)$callbackData)) {
            $states[$chat_id]['step'] = (string)$callbackData;
            saveStates($states, $state_file);
            exit;
        }

        if ($callbackData === 'transfer_services_support_help') {
            $states[$chat_id]['step'] = 'transfer_services_support_help';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🆘 <b>Transfer Services Help</b>

This section provides support and guidance for International Transfer, SEPA Transfer, and Wallet Transfer services.

For International Transfer activation, please complete the required verification process first.

For direct banking assistance, you may also use the FinoviaPay Premium Support Centre from the main menu.",
                [
                    [
                        ['text' => '📂 Transfer Management', 'callback_data' => 'transfer_management_menu']
                    ],
                    [
                        ['text' => '↩️ Return to Transfer Services', 'callback_data' => 'transfer_services_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'sepa_transfer_service_notice' || $callbackData === 'wallet_transfer_service_notice') {
            sendInlineKeyboard(
                $chat_id,
                "ℹ️ <b>Service Notice</b>

This transfer management section has been added to the menu structure successfully. Its full customer workflow will be connected in the next development step.",
                [[['text' => '↩️ Return to Transfer Management', 'callback_data' => 'transfer_management_menu']]]
            );
            exit;
        }

        if ($callbackData === 'blocked_account_menu') {
            $states[$chat_id]['step'] = 'blocked_account_menu';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🛡️ <b>FinoviaPay Blocked Account Recovery</b>

If your account access is currently restricted, please choose one of the options below to submit a recovery request or contact our support team for further assistance.",
                botBlockedAccountMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'new_customer_open_account') {
            $states[$chat_id]['step'] = 'new_customer_open_account';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "🏦 <b>Open a New FinoviaPay Account</b>

Thank you for your interest in joining FinoviaPay. The premium Telegram onboarding workflow is currently being prepared for secure administrative activation.

For account opening guidance, eligibility information, or registration assistance, please contact our support team using the option below.",
                [
                    [
                        ['text' => '🎖️ Registration Assistance', 'callback_data' => 'new_customer_talk_to_support']
                    ],
                    [
                        ['text' => '↩️ Return to New Customer Menu', 'callback_data' => 'new_customer_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'new_customer_check_registration_status') {
            $states[$chat_id]['step'] = 'new_customer_check_registration_status';
            saveStates($states, $state_file);
            sendInlineKeyboard(
                $chat_id,
                "📋 <b>Registration Status Enquiry</b>

No active Telegram onboarding request is currently linked with this chat session. Once the registration module is enabled and your request has been submitted, you will be able to review its status from this section.",
                [
                    [
                        ['text' => '↩️ Return to New Customer Menu', 'callback_data' => 'new_customer_menu']
                    ]
                ]
            );
            exit;
        }

        if ($callbackData === 'new_customer_talk_to_support') {
            $callbackData = 'talk_to_agent_unverified';
        }

        if ($callbackData === 'support_help_live_chat') {
            $callbackData = 'talk_to_agent_unverified';
        }

        if ($callbackData === 'support_help_create_ticket') {
            $callbackData = 'create_support_ticket';
        }

        if ($callbackData === 'already_customer') {
            $states[$chat_id] = ['step' => 'await_mobile'];

            sendMessage(
                $chat_id,
                "For account identification, please enter your <b>registered mobile number</b> exactly as it appears on your Finoviapay profile.\n\nExample:\n+34600000000"
            );

            saveStates($states, $state_file);
            exit;
        }

        if ($callbackData === 'not_registered') {
            unset($states[$chat_id]);
            saveStates($states, $state_file);

            sendMessage(
                $chat_id,
                "Thank you for contacting FinoviaPay.\n\nOur records indicate that you have selected the non-registered customer option.\n\nTo access full banking support services, please complete your registration through our official platform.\n\nRegistration Link:\nhttps://finoviapay.com/register"
            );
            exit;
        }

        if ($callbackData === 'retry_mobile') {
            $states[$chat_id] = ['step' => 'await_mobile'];
            saveStates($states, $state_file);

            sendMessage(
                $chat_id,
                "Please re-enter your registered mobile number exactly as it appears on your Finoviapay profile.\n\nExample:\n+34600000000"
            );
            exit;
        }

        if ($callbackData === 'talk_to_agent_unverified') {
            $enteredMobile = $states[$chat_id]['entered_mobile'] ?? null;

            if (!botIsLiveChatEnabled($pdo)) {
                sendInlineKeyboard(
                    $chat_id,
                    botLiveChatUnavailableMessage(),
                    botLiveChatUnavailableKeyboard()
                );
                exit;
            }

            $existingChat = getOpenSupportChat($pdo, $chat_id);

            if ($existingChat) {
                sendMessage(
                    $chat_id,
                    "Your live support request is already active in our secure support system.\n\nPlease remain connected while the assigned or next available FinoviaPay Support Officer continues handling your request."
                );
                exit;
            }

            $chat = createSupportChat($pdo, $chat_id, null);

            if ($enteredMobile) {
                $upd = $pdo->prepare("UPDATE support_chats SET customer_mobile = :mobile, updated_at = NOW() WHERE id = :id");
                $upd->execute([
                    ':mobile' => $enteredMobile,
                    ':id' => $chat['id']
                ]);
            }

            $waitingMsg = "Thank you for contacting FinoviaPay Customer Support.\n\nYour request to speak with a FinoviaPay Support Officer has been successfully received and securely registered in our customer support system.\n\nSupport Ticket ID: {$chat['ticket_id']}\nCurrent Queue Position: #{$chat['queue_position']}\nEstimated Waiting Time: {$chat['estimated_wait_minutes']} minutes\n\nAt this time, your mobile number could not be matched against our registered database records. However, your request may still be reviewed by a support officer. Verified customers may be prioritized where applicable.\n\nSecurity Notice\nFinoviaPay support officers will NEVER ask for your account password, card PIN, or one-time verification codes (OTP). If anyone requests this information, please report the incident immediately.\n\nPlease remain connected while we assign the next available banking support officer to your request.\n\nOnce an officer joins your conversation, you will receive a confirmation message together with the officer’s name, Banker Officer ID, and department details.\n\nWe appreciate your patience and thank you for choosing FinoviaPay.";

            sendMessage($chat_id, $waitingMsg);
            insertSupportMessage($pdo, $chat['id'], 'system', 'Unverified customer requested live agent support after mobile number did not match. Waiting notice with ticket details and security notice sent to customer.');
            exit;
        }



        if (strpos((string)$callbackData, 'rate_chat_') === 0) {
            $rating = (int) str_replace('rate_chat_', '', (string)$callbackData);
            if ($rating >= 1 && $rating <= 5) {
                $latestChat = getLatestSupportChatAnyStatus($pdo, $chat_id);
                if ($latestChat) {
                    $stmt = $pdo->prepare("INSERT INTO chat_ratings (chat_id, rating, created_at) VALUES (:chat_id, :rating, NOW())");
                    $stmt->execute([
                        ':chat_id' => $latestChat['id'],
                        ':rating' => $rating
                    ]);

                    $upd = $pdo->prepare("UPDATE support_chats SET rating_received = 1, updated_at = NOW() WHERE id = :id");
                    $upd->execute([':id' => $latestChat['id']]);
                }

                sendMessage($chat_id, "Thank you for rating your recent FinoviaPay support experience. Your feedback has been securely recorded and will help us improve our digital banking support services.");
            }
            exit;
        }

        if ($callbackData === 'send_transcript') {
            $latestChat = getLatestSupportChatAnyStatus($pdo, $chat_id);

            if (!$latestChat) {
                sendMessage($chat_id, "We were unable to locate a completed support session for transcript processing at this time. Please contact FinoviaPay Customer Support again if you require further assistance.");
                exit;
            }

            $transcriptText = buildTranscriptText($pdo, (int)$latestChat['id']);
            $customerEmail = $latestChat['customer_email'] ?? null;

            $stmt = $pdo->prepare("
                INSERT INTO chat_transcripts (chat_id, customer_email, transcript_text, sent_status, created_at)
                VALUES (:chat_id, :customer_email, :transcript_text, 0, NOW())
            ");
            $stmt->execute([
                ':chat_id' => $latestChat['id'],
                ':customer_email' => $customerEmail,
                ':transcript_text' => $transcriptText
            ]);

            $upd = $pdo->prepare("UPDATE support_chats SET transcript_sent = 1, updated_at = NOW() WHERE id = :id");
            $upd->execute([':id' => $latestChat['id']]);

            if (!empty($customerEmail)) {
                sendMessage($chat_id, "Your chat transcript request has been successfully registered. A copy of your completed support conversation has been queued for delivery to your registered email address: {$customerEmail}.");
            } else {
                sendMessage($chat_id, "Your chat transcript request has been successfully registered. However, no verified email address is currently available in our records for this support session.");
            }
            exit;
        }


        if ($callbackData === 'secure_direct_communication') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            $thread = null;
            if (function_exists('botTelegramDirectGetOpenThreadByTelegramId')) {
                $thread = botTelegramDirectGetOpenThreadByTelegramId($pdo, (string)$chat_id);
            }
            if (!$thread && $userId > 0 && function_exists('botTelegramDirectGetOpenThreadByUserId')) {
                $thread = botTelegramDirectGetOpenThreadByUserId($pdo, $userId);
            }

            if (!$thread) {
                sendInlineKeyboard($chat_id, botSecureDirectCommunicationNoThreadMessage(), botSecureDirectCommunicationNoThreadButtons());
                exit;
            }

            if (function_exists('botTelegramDirectMarkThreadReadForCustomer')) {
                botTelegramDirectMarkThreadReadForCustomer($pdo, (string)($thread['thread_id'] ?? ''));
            }

            sendInlineKeyboard(
                $chat_id,
                botSecureDirectCommunicationSummaryMessage($pdo, $thread),
                botSecureDirectCommunicationMenuButtons()
            );
            exit;
        }

        if ($callbackData === 'secure_direct_view') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            $thread = null;
            if (function_exists('botTelegramDirectGetOpenThreadByTelegramId')) {
                $thread = botTelegramDirectGetOpenThreadByTelegramId($pdo, (string)$chat_id);
            }
            if (!$thread && $userId > 0 && function_exists('botTelegramDirectGetOpenThreadByUserId')) {
                $thread = botTelegramDirectGetOpenThreadByUserId($pdo, $userId);
            }

            if (!$thread) {
                sendInlineKeyboard($chat_id, botSecureDirectCommunicationNoThreadMessage(), botSecureDirectCommunicationNoThreadButtons());
                exit;
            }

            if (function_exists('botTelegramDirectMarkThreadReadForCustomer')) {
                botTelegramDirectMarkThreadReadForCustomer($pdo, (string)($thread['thread_id'] ?? ''));
            }

            sendInlineKeyboard(
                $chat_id,
                botSecureDirectCommunicationHistoryMessage($pdo, $thread, 10),
                botSecureDirectCommunicationViewButtons()
            );
            exit;
        }

        if ($callbackData === 'secure_direct_reply') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            $thread = null;
            if (function_exists('botTelegramDirectGetOpenThreadByTelegramId')) {
                $thread = botTelegramDirectGetOpenThreadByTelegramId($pdo, (string)$chat_id);
            }
            if (!$thread && $userId > 0 && function_exists('botTelegramDirectGetOpenThreadByUserId')) {
                $thread = botTelegramDirectGetOpenThreadByUserId($pdo, $userId);
            }

            if (!$thread) {
                sendInlineKeyboard($chat_id, botSecureDirectCommunicationNoThreadMessage(), botSecureDirectCommunicationNoThreadButtons());
                exit;
            }

            $states[$chat_id]['step'] = 'secure_direct_communication_reply';
            saveStates($states, $state_file);

            sendInlineKeyboard(
                $chat_id,
                "✉️ <b>Reply to FinoviaPay Officer</b>\n\nPlease type your message below. Your reply will be securely delivered to the assigned FinoviaPay department.\n\n<b>Reference:</b> " . htmlspecialchars((string)($thread['thread_id'] ?? 'N/A'), ENT_QUOTES, 'UTF-8'),
                botSecureDirectCommunicationReplyButtons()
            );
            exit;
        }

        if ($callbackData === 'my_notifications') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            if ($userId <= 0) {
                sendMessage($chat_id, "Please complete account verification first to access your notification history.");
                exit;
            }

            sendMyNotificationsMenu($pdo, $chat_id, $userId);
            exit;
        }

        if ($callbackData === 'wallet_balance') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            if ($userId <= 0) {
                sendMessage($chat_id, "Please complete account verification first to access your balance summary.");
                exit;
            }

            sendWalletBalanceMenu($pdo, $chat_id, $userId);
            exit;
        }

        if ($callbackData === 'wallet_transactions') {
            $userId = (int)($states[$chat_id]['user']['id'] ?? $states[$chat_id]['identified_user_id'] ?? 0);

            if ($userId <= 0) {
                sendMessage($chat_id, "Please complete account verification first to access your transaction history.");
                exit;
            }

            sendWalletTransactionsMenu($pdo, $chat_id, $userId);
            exit;
        }

        if ($callbackData === 'wallet_transfer') {
            sendMessage($chat_id, "You have selected <b>Wallet Transfer Support</b>.\n\nPlease send a brief description of your issue, and a support officer will review it accordingly.");
            exit;
        }

        if ($callbackData === 'kyc_verification') {
            sendMessage($chat_id, "You have selected <b>KYC Verification Support</b>.\n\nPlease provide a brief explanation of your query, and our compliance support team will review your request.");
            exit;
        }

        if (
            $callbackData === 'card_order' ||
            strpos((string)$callbackData, 'ticket_card_type_') === 0 ||
            strpos((string)$callbackData, 'ticket_issue_') === 0 ||
            strpos((string)$callbackData, 'physical_card_') === 0 ||
            strpos((string)$callbackData, 'physical_card_view_') === 0 ||
            in_array(($states[$chat_id]['step'] ?? ''), ['ticket_card_type', 'ticket_issue_type', 'physical_card_menu'], true)
        ) {
            require __DIR__ . '/card_services.php';
            exit;
        }

        if (
            $callbackData === 'my_support_tickets' ||
            strpos((string)$callbackData, 'my_tickets_') === 0
        ) {
            require __DIR__ . '/my_support_tickets.php';
            exit;
        }


        if ($callbackData === 'create_support_ticket') {
            botHandleSupportTicketCallback($pdo, $chat_id, $states, $state_file);
            exit;
        }

        if ($callbackData === 'create_card_ticket') {
            $states[$chat_id]['step'] = 'ticket_card_type';
            saveStates($states, $state_file);

            $buttons = [
                [
                    ['text' => '💳 Physical Card Support', 'callback_data' => 'ticket_card_type_physical'],
                    ['text' => '🪪 Virtual Card Support', 'callback_data' => 'ticket_card_type_virtual']
                ],
                [
                    ['text' => '🔐 One-Time Card Support', 'callback_data' => 'ticket_card_type_onetime']
                ],
                [
                    ['text' => '↩️ Back to Main Menu', 'callback_data' => 'main_menu']
                ]
            ];

            sendInlineKeyboard($chat_id, "💳 <b>Manage a Card</b>

Please select your card type to continue with secure card support.", $buttons);
            exit;
        }

        if (strpos((string)$callbackData, 'ticket_card_type_') === 0) {
            $cardTypeMap = [
                'ticket_card_type_virtual' => 'Virtual Card',
                'ticket_card_type_physical' => 'Physical Card',
                'ticket_card_type_onetime' => 'One-Time Card'
            ];
            $selectedCardType = $cardTypeMap[$callbackData] ?? 'Card';

            $states[$chat_id]['step'] = 'ticket_issue_type';
            $states[$chat_id]['ticket_flow']['card_type'] = $selectedCardType;
            saveStates($states, $state_file);

            $buttons = [
                [
                    ['text' => 'Card Declined', 'callback_data' => 'ticket_issue_declined'],
                    ['text' => 'Online Payment Failed', 'callback_data' => 'ticket_issue_online_failed']
                ],
                [
                    ['text' => 'Card Not Activated', 'callback_data' => 'ticket_issue_not_activated'],
                    ['text' => 'Card Blocked', 'callback_data' => 'ticket_issue_blocked']
                ],
                [
                    ['text' => 'Other Issue', 'callback_data' => 'ticket_issue_other']
                ]
            ];

            sendInlineKeyboard($chat_id, "Please select the issue you are experiencing.", $buttons);
            exit;
        }

        if (strpos((string)$callbackData, 'ticket_issue_') === 0) {
            $issueMap = [
                'ticket_issue_declined' => 'Card Declined',
                'ticket_issue_online_failed' => 'Online Payment Failed',
                'ticket_issue_not_activated' => 'Card Not Activated',
                'ticket_issue_blocked' => 'Card Blocked',
                'ticket_issue_other' => 'Other Issue'
            ];
            $selectedIssue = $issueMap[$callbackData] ?? 'Other Issue';

            $states[$chat_id]['step'] = 'ticket_description';
            $states[$chat_id]['ticket_flow']['issue_type'] = $selectedIssue;
            saveStates($states, $state_file);

            sendMessage(
                $chat_id,
                "Please briefly describe the issue you are experiencing with your card.\n\nOur Card Operations Department will review your request once your ticket is submitted."
            );
            exit;
        }

        if ($callbackData === 'account_status') {
            sendMessage($chat_id, "You have selected <b>Account Status Support</b>.\n\nPlease send your request in detail for further review.");
            exit;
        }

        if ($callbackData === 'talk_to_agent') {
            $verifiedUser = $states[$chat_id]['user'] ?? null;

            if (!botIsLiveChatEnabled($pdo)) {
                sendInlineKeyboard(
                    $chat_id,
                    botLiveChatUnavailableMessage(),
                    botLiveChatUnavailableKeyboard()
                );
                exit;
            }

            $existingChat = getOpenSupportChat($pdo, $chat_id);

            if ($existingChat) {
                sendMessage(
                    $chat_id,
                    "Your live support request is already active in our secure support system.\n\nPlease remain connected while the assigned or next available FinoviaPay Support Officer continues handling your request."
                );
                exit;
            }

            $chat = createSupportChat($pdo, $chat_id, $verifiedUser);

            $waitingMsg = "Thank you for contacting FinoviaPay Customer Support.\n\nYour request to speak with a FinoviaPay Support Officer has been successfully received and securely registered in our customer support system.\n\nSupport Ticket ID: {$chat['ticket_id']}\nCurrent Queue Position: #{$chat['queue_position']}\nEstimated Waiting Time: {$chat['estimated_wait_minutes']} minutes\n\nSecurity Notice\nFinoviaPay support officers will NEVER ask for your account password, card PIN, or one-time verification codes (OTP). If anyone requests this information, please report the incident immediately.\n\nPlease remain connected while we assign the next available banking support officer to your request.\n\nOnce an officer joins your conversation, you will receive a confirmation message together with the officer’s name, Banker Officer ID, and department details.\n\nIn the meantime, you may continue to type your message here. Your information will remain securely available for review by the assigned support officer.\n\nWe appreciate your patience and thank you for choosing FinoviaPay.";

            sendMessage($chat_id, $waitingMsg);
            insertSupportMessage($pdo, $chat['id'], 'system', 'Customer requested live agent support. Waiting notice with ticket details and security notice sent to customer.');
            exit;
        }
    }


    /* ===============================
       VEHICLE FINANCE APPLY TEXT ROUTER
       Minimum touch: forwards normal text replies (Suzuki, model, income, witness info etc.)
       to the separate vehicle_finance_apply.php module through vehicle_finance_services.php.
       =============================== */
    if (function_exists('botHandleVehicleFinanceText')) {
        if (botHandleVehicleFinanceText($pdo, $states, $state_file, $chat_id, (string)$text, $telegram_user_id ?? null)) {
            exit;
        }
    }


    if (is_array($state) && ($state['step'] ?? '') === 'telegram_rebind_await_full_name' && $text !== '') {
        $fullName = trim($text);

        if ($fullName === '') {
            sendMessage($chat_id, "Please enter your full name to continue.");
            exit;
        }

        $states[$chat_id]['telegram_rebind']['full_name'] = $fullName;
        $states[$chat_id]['step'] = 'telegram_rebind_await_mobile';
        saveStates($states, $state_file);

        sendMessage(
            $chat_id,
            "Please enter your registered mobile number linked to your FinoviaPay account."
        );
        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'telegram_rebind_await_mobile' && $text !== '') {
        $mobile = trim($text);

        if ($mobile === '') {
            sendMessage($chat_id, "Please enter your registered mobile number.");
            exit;
        }

        $states[$chat_id]['telegram_rebind']['registered_mobile'] = $mobile;
        $states[$chat_id]['step'] = 'telegram_rebind_await_reason';
        saveStates($states, $state_file);

        sendMessage(
            $chat_id,
            "Please explain why you want to use your FinoviaPay account on this Telegram account."
        );
        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'telegram_rebind_await_reason' && $text !== '') {
        $reason = trim($text);

        if ($reason === '') {
            sendMessage($chat_id, "Please enter the reason for your Telegram re-bind request.");
            exit;
        }

        $states[$chat_id]['telegram_rebind']['reason'] = $reason;
        saveStates($states, $state_file);

        $telegramProfile = botGetTelegramProfileFromUpdate($update);

        try {
            $requestId = botInsertTelegramRebindRequest(
                $pdo,
                $states[$chat_id] ?? [],
                $telegramProfile
            );

            $states[$chat_id]['step'] = 'telegram_binding_restricted';
            saveStates($states, $state_file);

            sendInlineKeyboard(
                $chat_id,
                botFormatTelegramRebindSubmittedMessage($requestId),
                botTelegramRebindMainMenuKeyboard()['inline_keyboard']
            );
        } catch (Throwable $e) {
            error_log("BOT ERROR: Telegram re-bind request submit failed: " . $e->getMessage());

            sendMessage(
                $chat_id,
                "❌ <b>Request Submission Failed</b>\n\nWe were unable to submit your Telegram re-bind request at this time. Please try again later or contact customer support."
            );
        }

        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'await_mobile' && $text !== '') {
        try {
            $mobile = normalizeMobile($text);
            botServiceGateLog('await_mobile start | chat_id=' . $chat_id . ' | raw=' . $text . ' | normalized=' . $mobile);

            $mobileUser = findUserByMobile($pdo, $mobile);
            botServiceGateLog('await_mobile user_lookup=' . json_encode($mobileUser, JSON_UNESCAPED_UNICODE));

            if ($mobileUser) {
                $userId = (int)($mobileUser['id'] ?? 0);
                $hasAccess = ($userId > 0) ? botUserHasDigitalBankingAccess($pdo, $userId, 'digital_banking') : false;
                botServiceGateLog('await_mobile access_check | user_id=' . $userId . ' | has_access=' . ($hasAccess ? 'yes' : 'no'));

                if (!$hasAccess) {
                    $states[$chat_id] = [
                        'step' => 'digital_banking_service_disabled',
                        'entered_mobile' => $mobile,
                        'identified_user_id' => $userId,
                        'user' => [
                            'id' => $mobileUser['id'] ?? null,
                            'name' => $mobileUser['name'] ?? 'Customer',
                            'mobile' => $mobileUser['mobile'] ?? $mobile,
                            'dob' => $mobileUser['dob'] ?? '',
                            'email' => $mobileUser['email'] ?? '',
                            'account_number' => $mobileUser['account_number'] ?? ''
                        ]
                    ];
                    saveStates($states, $state_file);

                    sendInlineKeyboard(
                        $chat_id,
                        botDigitalBankingDisabledMessage(),
                        botDigitalBankingDisabledKeyboard()
                    );
                    botServiceGateLog('await_mobile blocked response sent');
                    exit;
                }

                $states[$chat_id] = [
                    'step' => 'await_dob',
                    'mobile' => $mobile
                ];
                saveStates($states, $state_file);

                sendMessage(
                    $chat_id,
                    "Thank you.\n\nNow please enter your <b>Date of Birth</b> for secure verification.\n\nAccepted format:\nYYYY-MM-DD\n\nExample:\n1996-08-12"
                );
                botServiceGateLog('await_mobile DOB request sent');
                exit;
            }

            $states[$chat_id] = [
                'step' => 'mobile_not_found',
                'entered_mobile' => $mobile
            ];
            saveStates($states, $state_file);

            $buttons = [
                [
                    ['text' => 'Try Again', 'callback_data' => 'retry_mobile'],
                    ['text' => 'Talk to Banker Officer', 'callback_data' => 'talk_to_agent_unverified']
                ]
            ];

            sendInlineKeyboard(
                $chat_id,
                "The mobile number you entered could not be matched with our registered system records.\n\nPlease review your number carefully and try again.\n\nIf you would still like to speak with one of our banker officers, please select the option below.",
                $buttons
            );
            botServiceGateLog('await_mobile mobile not found response sent');
            exit;
        } catch (Throwable $e) {
            botServiceGateLog('await_mobile fatal=' . $e->getMessage());
            sendMessage(
                $chat_id,
                "We were unable to complete the secure mobile verification step at this time. Please try again in a moment or contact FinoviaPay Customer Service for assistance."
            );
            exit;
        }
    }

    if (is_array($state) && ($state['step'] ?? '') === 'await_dob' && $text !== '') {
        $mobile = $state['mobile'] ?? '';
        $dob = normalizeDob($text);

        if (!$dob) {
            sendMessage($chat_id, "The Date of Birth format could not be recognized.\n\nPlease enter it in the following format:\nYYYY-MM-DD\n\nExample:\n1996-08-12");
            exit;
        }

        $matchedRows = debugUserMatchesByMobileDob($pdo, $mobile, $dob);
        if (function_exists('bot_debug_log')) {
            bot_debug_log('DOB verification full matches=' . json_encode($matchedRows, JSON_UNESCAPED_UNICODE));
        }
        blocked_debug_log('DOB verification full matches=' . json_encode($matchedRows, JSON_UNESCAPED_UNICODE));

        $blockedUser = findBlockedUserByMobileDob($pdo, $mobile, $dob);
        if (function_exists('bot_debug_log')) {
            bot_debug_log('DOB verification blocked match=' . (is_array($blockedUser) ? ('yes user_id=' . ($blockedUser['id'] ?? '') . ' status=' . ($blockedUser['status'] ?? '')) : 'no'));
        }
        blocked_debug_log('DOB verification blocked match=' . (is_array($blockedUser) ? ('yes user_id=' . ($blockedUser['id'] ?? '') . ' status=' . ($blockedUser['status'] ?? '')) : 'no'));

        if ($blockedUser) {
            $states[$chat_id] = [
                'step' => 'blocked',
                'identified_user_id' => (int)($blockedUser['id'] ?? 0),
                'user' => [
                    'id' => $blockedUser['id'] ?? null,
                    'name' => $blockedUser['name'] ?? 'Customer',
                    'mobile' => $blockedUser['mobile'] ?? $mobile,
                    'dob' => $blockedUser['dob'] ?? $dob,
                    'email' => $blockedUser['email'] ?? '',
                    'account_number' => $blockedUser['account_number'] ?? ''
                ]
            ];
            saveStates($states, $state_file);

            sendBlockedAccountNotice($pdo, $chat_id, $states);
            exit;
        }

        $user = findUserByMobileDob($pdo, $mobile, $dob);
        if (function_exists('bot_debug_log')) {
            bot_debug_log('DOB verification active/general match=' . (is_array($user) ? ('yes user_id=' . ($user['id'] ?? '') . ' status=' . ($user['status'] ?? '')) : 'no'));
        }
        blocked_debug_log('DOB verification active/general match=' . (is_array($user) ? ('yes user_id=' . ($user['id'] ?? '') . ' status=' . ($user['status'] ?? '')) : 'no'));

        if ($user) {
            $states[$chat_id] = [
                'step' => 'verified',
                'identified_user_id' => (int)($user['id'] ?? 0),
                'user' => [
                    'id' => $user['id'] ?? null,
                    'name' => $user['name'] ?? 'Customer',
                    'mobile' => $user['mobile'] ?? $mobile,
                    'dob' => $user['dob'] ?? $dob,
                    'email' => $user['email'] ?? '',
                    'account_number' => $user['account_number'] ?? ''
                ]
            ];
            saveStates($states, $state_file);

            $userId = (int)($user['id'] ?? 0);
            $otpMode = botGetSetting($pdo, 'telegram_bot_email_otp_mode', 'enabled');

            if ($otpMode === 'disabled') {
                $bindingCheck = botHandleTelegramBindingAfterVerification(
                    $pdo,
                    $update,
                    $states,
                    $state_file,
                    $chat_id,
                    $states[$chat_id]['user'] ?? $user
                );

                if (empty($bindingCheck['allowed'])) {
                    sendInlineKeyboard(
                        $chat_id,
                        (string)($bindingCheck['message'] ?? botFormatTelegramBindingRestrictedMessage()),
                        $bindingCheck['buttons'] ?? botTelegramBindingRestrictedButtons()
                    );
                    exit;
                }

                botStartSecureSession($states, $state_file, $chat_id, 5);
                sendMessage($chat_id, "✅ <b>Identification Successful</b>\n\nYour account identification has been completed successfully.\n\nEmail OTP verification is currently disabled by the authorized FinoviaPay admin team, so you may continue directly to protected services.");
                sendMessage($chat_id, botSecureSessionTimeoutMessage(5));
                if (isset($states[$chat_id]['user']) && is_array($states[$chat_id]['user'])) {
                    sendVerifiedWelcomeMenu($pdo, $chat_id, $states[$chat_id]['user']);
                }
                exit;
            }

            $otpStart = startBotEmailOtpFlow($pdo, $states, $state_file, $chat_id, $userId);

            if (!empty($otpStart['ok'])) {
                sendMessage($chat_id, $otpStart['message'], botEmailOtpKeyboard());
                exit;
            } else {
                sendMessage($chat_id, $otpStart['message']);
                exit;
            }
        } else {
            sendMessage($chat_id, "We were unable to verify your details against our registered records.\n\nPlease review your mobile number and date of birth carefully, then type /start to begin the identification process again.");
            unset($states[$chat_id]);
            saveStates($states, $state_file);
            exit;
        }
    }

    if (is_array($state) && ($state['step'] ?? '') === 'awaiting_email_otp' && $text !== '') {
        $verify = handleBotEmailOtpInput($pdo, $states, $state_file, $chat_id, $text);

        if (!empty($verify['ok'])) {
            $bindingCheck = botHandleTelegramBindingAfterVerification(
                $pdo,
                $update,
                $states,
                $state_file,
                $chat_id,
                $states[$chat_id]['user'] ?? []
            );

            if (empty($bindingCheck['allowed'])) {
                sendInlineKeyboard(
                    $chat_id,
                    (string)($bindingCheck['message'] ?? botFormatTelegramBindingRestrictedMessage()),
                    $bindingCheck['buttons'] ?? botTelegramBindingRestrictedButtons()
                );
                exit;
            }

            $sessionTimeoutMode = botGetSetting($pdo, 'telegram_bot_session_timeout_mode', 'enabled');
            if ($sessionTimeoutMode === 'enabled') {
                botStartSecureSession($states, $state_file, $chat_id, 5);
            } else {
                if (isset($states[$chat_id]) && is_array($states[$chat_id])) {
                    unset($states[$chat_id]['session_verified'], $states[$chat_id]['last_activity'], $states[$chat_id]['session_expires_at'], $states[$chat_id]['session_timeout_minutes']);
                    saveStates($states, $state_file);
                }
            }

            sendMessage($chat_id, $verify['message']);
            if ($sessionTimeoutMode === 'enabled') {
                sendMessage($chat_id, botSecureSessionTimeoutMessage(5));
            }
            if (isset($states[$chat_id]['user']) && is_array($states[$chat_id]['user'])) {
                sendVerifiedWelcomeMenu($pdo, $chat_id, $states[$chat_id]['user']);
            }
        } else {
            sendMessage($chat_id, $verify['message'], botEmailOtpKeyboard());
        }
        exit;
    }

    if (botHandleAccountOpeningReviewTextCompat($pdo, $states, $state_file, $chat_id, (string)$text, $telegram_user_id)) {
        exit;
    }

    if (botHandleNewAccountTextStep($pdo, $states, $state_file, $chat_id, (string)$text, $telegram_user_id)) {
        exit;
    }

    if (botHandleBlockedAccountRecoveryMessage($pdo, $update, $states, $state_file, $chat_id, (string)$text)) {
        exit;
    }

    $digitalServiceTextResponse = botHandleDigitalServiceTextStep($pdo, $states, $state_file, $chat_id, (string)$text);
    if (is_array($digitalServiceTextResponse)) {
        sendInlineKeyboard(
            $chat_id,
            (string)($digitalServiceTextResponse['text'] ?? ''),
            $digitalServiceTextResponse['buttons'] ?? []
        );
        exit;
    }

    if (botHandleIntTransferTextStep($pdo, $states, $state_file, $chat_id, (string)$text)) {
        exit;
    }

    if (botHandleSupportTicketSteps($pdo, $chat_id, (string)$text, $states, $state_file)) {
        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'ticket_subject' && $text !== '') {
        $subject = trim($text);

        if ($subject === '') {
            sendMessage($chat_id, "Please enter the subject of your request to continue.");
            exit;
        }

        $states[$chat_id]['support_ticket_flow']['subject'] = $subject;
        $states[$chat_id]['step'] = 'ticket_issue';
        saveStates($states, $state_file);

        sendMessage(
            $chat_id,
            "Thank you.\n\nPlease describe your issue in detail so our support team can review and assist you accordingly."
        );
        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'ticket_issue' && $text !== '') {
        $subject = trim((string)($states[$chat_id]['support_ticket_flow']['subject'] ?? 'General Support'));
        $description = trim($text);

        if ($description === '') {
            sendMessage($chat_id, "Please describe your issue in detail to continue.");
            exit;
        }

        $ticketId = createGeneralSupportTicketRecord($pdo, $states[$chat_id], $subject, $description, $chat_id);

        unset($states[$chat_id]['support_ticket_flow']);
        if (isset($states[$chat_id]['user'])) {
            $states[$chat_id]['step'] = 'verified';
        } else {
            $states[$chat_id]['step'] = 'choose_customer_type';
        }
        saveStates($states, $state_file);

        sendMessage(
            $chat_id,
            "Your support ticket has been successfully created.\n\nTicket ID: {$ticketId}\nSubject: {$subject}\nDepartment: Customer Support Department\n\nYour request has been securely recorded and forwarded to the appropriate FinoviaPay support team for review.\n\nOur support officers will review the information you provided and respond as soon as possible.\n\nThank you for choosing FinoviaPay."
        );
        exit;
    }

    if (is_array($state) && ($state['step'] ?? '') === 'ticket_description' && $text !== '') {
        $cardType = $states[$chat_id]['ticket_flow']['card_type'] ?? 'Card';
        $issueType = $states[$chat_id]['ticket_flow']['issue_type'] ?? 'Other Issue';
        $ticketId = createSupportTicketRecord($pdo, $states[$chat_id], $cardType, $issueType, $text, $chat_id);

        unset($states[$chat_id]['ticket_flow']);
        if (isset($states[$chat_id]['user'])) {
            $states[$chat_id]['step'] = 'verified';
        } else {
            $states[$chat_id]['step'] = 'choose_customer_type';
        }
        saveStates($states, $state_file);

        sendMessage(
            $chat_id,
            "Your support ticket has been successfully created.\n\nTicket ID: {$ticketId}\nDepartment: Card Operations Department\n\nYour request has been forwarded to the FinoviaPay Card Operations Department for review.\n\nOur support team will examine the information you provided and will respond to your ticket as soon as possible.\n\nIf additional information is required, you may be contacted by a FinoviaPay Support Officer.\n\nThank you for choosing FinoviaPay."
        );
        exit;
    }


    if ($message && $text !== '' && $text !== '/start' && strpos((string)$text, '/') !== 0 && function_exists('botTelegramDirectHandleCustomerReply')) {
        $currentStep = (string)($states[$chat_id]['step'] ?? '');
        $hasDirectReplyContext = ($currentStep === 'secure_direct_communication_reply');
        $hasPendingInteractiveContext = botHasPendingInteractiveContext($states[$chat_id] ?? []);
        $canRouteDirectMessage = botTelegramDirectCanHandleTelegramReply($pdo, (string)$chat_id);

        if ($hasDirectReplyContext || (!$hasPendingInteractiveContext && $canRouteDirectMessage)) {
            $directReplyResult = botTelegramDirectHandleCustomerReply($pdo, (string)$chat_id, (string)$text, true);

            if (!empty($directReplyResult['status'])) {
                if (isset($states[$chat_id]['user']) && is_array($states[$chat_id]['user'])) {
                    $states[$chat_id]['step'] = 'verified';
                } else {
                    $states[$chat_id]['step'] = 'choose_customer_type';
                }
                saveStates($states, $state_file);

                if ($hasDirectReplyContext) {
                    sendInlineKeyboard(
                        $chat_id,
                        "✅ <b>Secure Reply Sent</b>\n\nYour message has been securely delivered to the assigned FinoviaPay officer.\n\nYou may return to the direct communication section at any time to review this conversation.",
                        [
                            [
                                ['text' => '📄 View Active Conversation', 'callback_data' => 'secure_direct_view']
                            ],
                            [
                                ['text' => '↩️ Return to Banking Menu', 'callback_data' => 'main_menu']
                            ]
                        ]
                    );
                }

                exit;
            }
        }
    }

    if ($message && $text !== '' && $text !== '/start') {
        $openChat = getOpenSupportChat($pdo, $chat_id);

        if ($openChat) {
            $lowerText = strtolower($text);
            $ignoreTexts = [
                'already a customer',
                'not registered yet',
                'wallet transfer',
                'kyc verification',
                'card order',
                'talk to agent',
                'account status'
            ];

            if (!in_array($lowerText, $ignoreTexts, true)) {
                insertSupportMessage($pdo, $openChat['id'], 'user', $text);
                $upd = $pdo->prepare("UPDATE support_chats SET updated_at = NOW() WHERE id = :id");
                $upd->execute([':id' => $openChat['id']]);
            }
        } elseif (!botHasPendingInteractiveContext($states[$chat_id] ?? [])) {
            sendMessage(
                $chat_id,
                "⚠️ <b>Unable to Process Request</b>

We could not match your message to any active FinoviaPay service or ongoing request at this time.

Please use the available menu options or select the appropriate banking service before sending your message."
            );
            exit;
        }
    }

} catch (Throwable $e) {
    error_log("BOT ERROR: " . $e->getMessage());
}

exit;
?>
