<?php

/**
 * Sequential queue for cart-to-cart pay approvals (one panel job at a time).
 */
class PayFulfillmentQueue
{
    private const LOCK_NAME = 'wizwiz_pay_fulfillment';
    private const STALE_SECONDS = 90;

    /**
     * Prepare pay for processing. Never blocks re-approval while queued/processing.
     *
     * @return 'not_found'|'approved'|'queued'|'kick'|'busy'
     */
    public static function acceptAndProcess(string $hashId): string
    {
        global $connection;

        self::requeueStale($connection);

        $stmt = $connection->prepare("SELECT `id`, `state`, `request_date` FROM `pays` WHERE `hash_id` = ? LIMIT 1");
        $stmt->bind_param('s', $hashId);
        $stmt->execute();
        $row = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        if (!$row) {
            return 'not_found';
        }
        if ($row['state'] === 'approved') {
            return 'approved';
        }

        $state = (string) $row['state'];
        $age = time() - (int) ($row['request_date'] ?? 0);

        if ($state === 'processing' && $age >= 30) {
            self::requeuePay($connection, $hashId);
            return 'kick';
        }
        if (in_array($state, ['queued', 'processing'], true)) {
            return 'kick';
        }

        $stmt = $connection->prepare(
            "UPDATE `pays` SET `state` = 'queued', `request_date` = ? WHERE `hash_id` = ? AND `state` IN ('have_sent', 'paid', 'paid_with_wallet')"
        );
        $now = time();
        $stmt->bind_param('is', $now, $hashId);
        $stmt->execute();
        $queued = $stmt->affected_rows > 0;
        $stmt->close();

        return $queued ? 'queued' : 'busy';
    }

    /** @deprecated Use acceptAndProcess() */
    public static function enqueue(string $hashId): string
    {
        $result = self::acceptAndProcess($hashId);
        if ($result === 'kick') {
            return 'already_queued';
        }
        return $result;
    }

    public static function getQueuePosition(string $hashId): int
    {
        global $connection;

        $stmt = $connection->prepare("SELECT `id` FROM `pays` WHERE `hash_id` = ? LIMIT 1");
        $stmt->bind_param('s', $hashId);
        $stmt->execute();
        $row = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        if (!$row) {
            return 0;
        }
        $payId = (int) $row['id'];

        $stmt = $connection->prepare(
            "SELECT COUNT(*) AS c FROM `pays` WHERE `state` IN ('queued', 'processing') AND `id` <= ?"
        );
        $stmt->bind_param('i', $payId);
        $stmt->execute();
        $count = (int) ($stmt->get_result()->fetch_assoc()['c'] ?? 0);
        $stmt->close();

        return max(1, $count);
    }

    public static function getPendingCount(): int
    {
        global $connection;
        $res = $connection->query("SELECT COUNT(*) AS c FROM `pays` WHERE `state` IN ('queued', 'processing')");
        if (!$res) {
            return 0;
        }
        return (int) ($res->fetch_assoc()['c'] ?? 0);
    }

    public static function processQueue(): void
    {
        global $connection;

        if (!self::acquireLock($connection)) {
            return;
        }

        @set_time_limit(0);
        @ignore_user_abort(true);

        try {
            self::requeueStale($connection);

            while ($payInfo = self::claimNext($connection)) {
                @set_time_limit(0);
                try {
                    self::fulfillCartPay($payInfo);
                } finally {
                    self::recoverStuckPay((string) $payInfo['hash_id'], $payInfo);
                }
            }
        } finally {
            self::releaseLock($connection);
        }
    }

    /** @return array<string, mixed>|null */
    private static function claimNext(mysqli $connection): ?array
    {
        $res = $connection->query(
            "SELECT * FROM `pays` WHERE `state` = 'queued' ORDER BY `id` ASC LIMIT 1"
        );
        if (!$res || $res->num_rows === 0) {
            return null;
        }
        $row = $res->fetch_assoc();
        $hashId = (string) $row['hash_id'];
        $now = time();

        $stmt = $connection->prepare(
            "UPDATE `pays` SET `state` = 'processing', `request_date` = ? WHERE `hash_id` = ? AND `state` = 'queued'"
        );
        $stmt->bind_param('is', $now, $hashId);
        $stmt->execute();
        $claimed = $stmt->affected_rows > 0;
        $stmt->close();

        return $claimed ? $row : self::claimNext($connection);
    }

    private static function requeueStale(mysqli $connection): void
    {
        $cutoff = time() - self::STALE_SECONDS;
        $stmt = $connection->prepare(
            "UPDATE `pays` SET `state` = 'queued' WHERE `state` = 'processing' AND `request_date` < ?"
        );
        $stmt->bind_param('i', $cutoff);
        $stmt->execute();
        $stmt->close();
    }

    private static function requeuePay(mysqli $connection, string $hashId): void
    {
        $stmt = $connection->prepare(
            "UPDATE `pays` SET `state` = 'queued' WHERE `hash_id` = ? AND `state` = 'processing'"
        );
        $stmt->bind_param('s', $hashId);
        $stmt->execute();
        $stmt->close();
    }

    private static function acquireLock(mysqli $connection): bool
    {
        $name = self::LOCK_NAME;
        $res = $connection->query("SELECT GET_LOCK('$name', 0)");
        if (!$res) {
            return false;
        }
        return (int) $res->fetch_row()[0] === 1;
    }

    private static function releaseLock(mysqli $connection): void
    {
        $name = self::LOCK_NAME;
        $connection->query("SELECT RELEASE_LOCK('$name')");
    }

    /** @param array<string, mixed> $payInfo */
    private static function recoverStuckPay(string $payHash, array $payInfo): void
    {
        global $connection;

        $stmt = $connection->prepare("SELECT `state` FROM `pays` WHERE `hash_id` = ? LIMIT 1");
        $stmt->bind_param('s', $payHash);
        $stmt->execute();
        $row = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        if (!$row || $row['state'] !== 'processing') {
            return;
        }

        if (getOrderByPayHash($payHash)) {
            markPayApproved($payHash);
            self::markAdminMessageDone($payInfo);
            return;
        }

        if (self::trySilentPanelComplete($payInfo)) {
            return;
        }

        requeuePayProcessing($payHash);
    }

    /**
     * Panel client exists but DB order missing — deliver without re-creating.
     *
     * @param array<string, mixed> $payInfo
     */
    private static function trySilentPanelComplete(array $payInfo): bool
    {
        global $connection, $botState;

        $payHash = (string) $payInfo['hash_id'];
        $uid = (int) $payInfo['user_id'];
        $fid = (int) $payInfo['plan_id'];
        $price = (int) $payInfo['price'];

        $stmt = $connection->prepare('SELECT * FROM `server_plans` WHERE `id`=?');
        $stmt->bind_param('i', $fid);
        $stmt->execute();
        $file_detail = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        if (!$file_detail || !planHasInbounds($file_detail)) {
            return false;
        }

        $server_id = (int) $file_detail['server_id'];
        if (!function_exists('isXuiPanel') || !isXuiPanel($server_id)) {
            return false;
        }

        $stmt = $connection->prepare('SELECT * FROM `server_info` WHERE `id`=?');
        $stmt->bind_param('i', $server_id);
        $stmt->execute();
        $serverInfo = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        $srv_remark = (string) ($serverInfo['remark'] ?? '');
        if (($botState['remark'] ?? '') === 'manual') {
            $remark = (string) ($payInfo['description'] ?? '');
        } else {
            $remark = buildOrderRemarkFromPay($payInfo, $srv_remark, $uid);
        }
        if ($remark === '' || !panelClientExistsForRemark($server_id, $remark)) {
            return false;
        }

        $service = getXuiV3Service($server_id);
        if (!$service->isClientReadyForDelivery($remark)) {
            return false;
        }

        self::completePanelOrder(
            $payInfo,
            $file_detail,
            $payHash,
            $uid,
            $fid,
            $server_id,
            (int) $file_detail['inbound_id'],
            (string) $file_detail['protocol'],
            (int) $file_detail['volume'],
            (int) $file_detail['days'],
            time() + (86400 * (int) $file_detail['days']),
            (int) ($price / max(1, (int) ($payInfo['agent_count'] ?? 1))),
            time(),
            (int) $file_detail['rahgozar'],
            $price
        );
        return true;
    }

    /** @param array<string, mixed> $payInfo */
    private static function handlePanelFailure(string $payHash, string $errMsg, ?array $serverInfo = null): void
    {
        global $admin;

        failPayProcessing($payHash, $errMsg);
        if (function_exists('isPanelTransientError') && isPanelTransientError($errMsg)) {
            sendMessage(
                "⏳ خطای موقت پنل — سفارش دوباره در صف قرار گرفت.\n\n" . $errMsg,
                null,
                null,
                $admin
            );
            return;
        }

        sendMessage('❌ | 😮 وای خطا داد لطفا سریع به مدیر بگو ...', null, null, $admin);
        $title = is_array($serverInfo) ? (string) ($serverInfo['title'] ?? '') : '';
        sendMessage("خطای سرور {$title}:\n\n" . $errMsg, null, null, $admin);
    }

    /**
     * Client exists on panel but order was never completed — finish attach + delivery.
     *
     * @param array<string, mixed> $payInfo
     * @param array<string, mixed> $file_detail
     * @param array<string, mixed> $serverInfo
     */
    private static function tryResumePartialPanelClient(
        array $payInfo,
        array $file_detail,
        array $serverInfo,
        string $srv_remark,
        string $payHash,
        int $uid,
        int $fid,
        int $server_id,
        int $inbound_id,
        string $protocol,
        int $volume,
        int $days,
        int $expire_date,
        int $eachPrice,
        int $date,
        int $rahgozar,
        int $price
    ): bool {
        global $botState;

        if (!function_exists('isXuiPanel') || !isXuiPanel($server_id) || !planHasInbounds($file_detail)) {
            return false;
        }

        if (($botState['remark'] ?? '') === 'manual') {
            $remark = (string) ($payInfo['description'] ?? '');
        } else {
            $remark = buildOrderRemarkFromPay($payInfo, $srv_remark, $uid);
        }
        if ($remark === '') {
            return false;
        }

        if (!function_exists('panelClientExistsForRemark') || !panelClientExistsForRemark($server_id, $remark)) {
            return false;
        }

        self::completePanelOrder(
            $payInfo,
            $file_detail,
            $payHash,
            $uid,
            $fid,
            $server_id,
            $inbound_id,
            $remark,
            $protocol,
            $volume,
            $days,
            $expire_date,
            $eachPrice,
            $date,
            $rahgozar,
            $price
        );
        return true;
    }

    /** @param array<string, mixed> $payInfo @param array<string, mixed> $file_detail */
    private static function completePanelOrder(
        array $payInfo,
        array $file_detail,
        string $payHash,
        int $uid,
        int $fid,
        int $server_id,
        int $inbound_id,
        string $remark,
        string $protocol,
        int $volume,
        int $days,
        int $expire_date,
        int $eachPrice,
        int $date,
        int $rahgozar,
        int $price
    ): void {
        global $connection, $buttonValues, $mainValues, $admin;

        if (getOrderByPayHash($payHash)) {
            markPayApproved($payHash);
            self::markAdminMessageDone($payInfo);
            return;
        }

        $keyboard = json_encode(['inline_keyboard' => [[['text' => $buttonValues['back_to_main'], 'callback_data' => 'mainMenu']]]]);
        $delivery = prepareXuiOrderDelivery($server_id, $remark, $fid, $inbound_id);
        $token = $delivery['token'];
        $uniqid = $delivery['uniqid'];
        $vray_link = $delivery['vray_link'];

        $agent_bought = (int) ($payInfo['agent_bought'] ?? 0);
        $stmt = $connection->prepare(
            "INSERT INTO `orders_list`
            (`userid`, `token`, `transid`, `fileid`, `server_id`, `inbound_id`, `remark`, `uuid`, `protocol`, `expire_date`, `link`, `amount`, `status`, `date`, `notif`, `rahgozar`, `agent_bought`)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,1, ?, 0, ?, ?)"
        );
        $stmt->bind_param(
            'sssiiisssisiiii',
            $uid,
            $token,
            $payHash,
            $fid,
            $server_id,
            $inbound_id,
            $remark,
            $uniqid,
            $protocol,
            $expire_date,
            $vray_link,
            $eachPrice,
            $date,
            $rahgozar,
            $agent_bought
        );
        $stmt->execute();
        $stmt->close();

        deliverXuiPanelOrderToCustomer($uid, $server_id, $remark, $keyboard, $fid, $inbound_id, $delivery);

        $adminChat = !empty($payInfo['chat_id']) ? $payInfo['chat_id'] : $admin;
        sendMessage(
            str_replace(['REMARK', 'VOLUME', 'DAYS'], [$remark, $volume, $days], $mainValues['sent_config_to_user']),
            null,
            'HTML',
            $adminChat
        );

        if (!planHasInbounds($file_detail)) {
            $stmt = $connection->prepare('UPDATE `server_info` SET `ucount` = `ucount` - 1 WHERE `id`=?');
            $stmt->bind_param('i', $server_id);
            $stmt->execute();
            $stmt->close();
        } else {
            $stmt = $connection->prepare('UPDATE `server_plans` SET `acount` = `acount` - 1 WHERE id=?');
            $stmt->bind_param('i', $fid);
            $stmt->execute();
            $stmt->close();
        }

        markPayApproved($payHash);
        self::markAdminMessageDone($payInfo);
        self::notifyInviteAndAdmin($payInfo, $file_detail, $remark, $price, $uid);
    }

    /** @param array<string, mixed> $payInfo */
    private static function fulfillCartPay(array $payInfo): void
    {
        global $connection, $botState, $admin, $buttonValues, $mainValues, $botUrl, $from_id;

        $payHash = (string) $payInfo['hash_id'];
        $uid = (int) $payInfo['user_id'];
        $fid = (int) $payInfo['plan_id'];

        if ($payInfo['type'] === 'RENEW_SCONFIG') {
            self::fulfillRenewPay($payInfo);
            return;
        }

        $stmt = $connection->prepare('SELECT * FROM `server_plans` WHERE `id`=?');
        $stmt->bind_param('i', $fid);
        $stmt->execute();
        $file_detail = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        if (!$file_detail) {
            releasePayProcessing($payHash);
            sendMessage('❌ پلن سفارش یافت نشد.', null, null, $admin);
            return;
        }

        $days = (int) $file_detail['days'];
        $date = time();
        $expire_microdate = (int) floor(microtime(true) * 1000) + (864000 * $days * 100);
        $expire_date = $date + (86400 * $days);
        $volume = (int) $file_detail['volume'];
        $protocol = (string) $file_detail['protocol'];
        $price = (int) $payInfo['price'];
        $server_id = (int) $file_detail['server_id'];
        $netType = (string) $file_detail['type'];
        $acount = (int) $file_detail['acount'];
        $inbound_id = (int) $file_detail['inbound_id'];
        $limitip = (int) $file_detail['limitip'];
        $rahgozar = (int) $file_detail['rahgozar'];
        $accountCount = ((int) ($payInfo['agent_count'] ?? 0)) !== 0 ? (int) $payInfo['agent_count'] : 1;
        $eachPrice = (int) ($price / $accountCount);

        if ($acount === 0 && planHasInbounds($file_detail)) {
            releasePayProcessing($payHash);
            sendMessage($mainValues['out_of_connection_capacity'], null, null, $admin);
            return;
        }

        if (!planHasInbounds($file_detail)) {
            $stmt = $connection->prepare('SELECT `ucount` FROM `server_info` WHERE `id`=?');
            $stmt->bind_param('i', $server_id);
            $stmt->execute();
            $ucount = (int) ($stmt->get_result()->fetch_assoc()['ucount'] ?? 0);
            $stmt->close();
            if ($ucount <= 0) {
                releasePayProcessing($payHash);
                sendMessage($mainValues['out_of_server_capacity'], null, null, $admin);
                return;
            }
        }

        $stmt = $connection->prepare('SELECT * FROM `server_info` WHERE `id`=?');
        $stmt->bind_param('i', $server_id);
        $stmt->execute();
        $serverInfo = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        $stmt = $connection->prepare('SELECT * FROM `server_config` WHERE `id`=?');
        $stmt->bind_param('i', $server_id);
        $stmt->execute();
        $serverConfig = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        $srv_remark = (string) ($serverInfo['remark'] ?? '');
        $serverType = (string) ($serverConfig['type'] ?? '');
        $portType = (string) ($serverConfig['port_type'] ?? '');

        $existingOrder = getOrderByPayHash($payHash);
        if ($existingOrder) {
            $remark = (string) $existingOrder['remark'];
            if (isXuiPanel((int) $existingOrder['server_id'])) {
                resendXuiPanelOrderToCustomer(
                    $uid,
                    (int) $existingOrder['server_id'],
                    $remark,
                    $existingOrder,
                    json_encode(['inline_keyboard' => [[['text' => $buttonValues['back_to_main'], 'callback_data' => 'mainMenu']]]]),
                    $fid
                );
            } else {
                deliverPanelOrder(
                    $uid,
                    (int) $existingOrder['server_id'],
                    $remark,
                    json_encode(['inline_keyboard' => [[['text' => $buttonValues['back_to_main'], 'callback_data' => 'mainMenu']]]]),
                    $fid,
                    (int) ($existingOrder['inbound_id'] ?? 0)
                );
            }
            markPayApproved($payHash);
            self::markAdminMessageDone($payInfo);
            $adminChat = !empty($payInfo['chat_id']) ? $payInfo['chat_id'] : $admin;
            sendMessage(
                str_replace(['REMARK', 'VOLUME', 'DAYS'], [$remark, $volume, $days], $mainValues['sent_config_to_user']),
                null,
                'HTML',
                $adminChat
            );
            return;
        }

        if (self::tryResumePartialPanelClient(
            $payInfo,
            $file_detail,
            $serverInfo,
            $srv_remark,
            $payHash,
            $uid,
            $fid,
            $server_id,
            $inbound_id,
            $protocol,
            $volume,
            $days,
            $expire_date,
            $eachPrice,
            $date,
            $rahgozar,
            $price
        )) {
            return;
        }

        if (!class_exists('QRcode', false)) {
            include __DIR__ . '/../phpqrcode/qrlib.php';
        }
        if (!defined('IMAGE_WIDTH')) {
            define('IMAGE_WIDTH', 540);
            define('IMAGE_HEIGHT', 540);
        }

        $remark = '';
        for ($i = 1; $i <= $accountCount; $i++) {
            $uniqid = generateRandomString(42, $protocol);

            $savedinfo = explode('-', (string) file_get_contents(__DIR__ . '/../settings/temp.txt'));
            $port = ((int) ($savedinfo[0] ?? 0)) + 1;
            $last_num = ((int) ($savedinfo[1] ?? 0)) + 1;

            if (($botState['remark'] ?? '') === 'manual') {
                $remark = (string) $payInfo['description'];
            } else {
                $remark = buildOrderRemarkFromPay($payInfo, $srv_remark, $uid);
            }

            if ($portType === 'auto') {
                file_put_contents(__DIR__ . '/../settings/temp.txt', $port . '-' . $last_num);
            } else {
                $port = rand(1111, 65000);
            }

            if (!planHasInbounds($file_detail)) {
                if (isUserApiPanelType($serverType)) {
                    $response = addUserApiPanelUser($server_id, $remark, $volume, $days, $fid);
                    if (!$response->success && ($response->msg ?? '') === 'User already exists') {
                        $remark .= (string) rand(1111, 99999);
                        $response = addUserApiPanelUser($server_id, $remark, $volume, $days, $fid);
                    }
                } else {
                    $response = addUser($server_id, $uniqid, $protocol, $port, $expire_microdate, $remark, $volume, $netType, 'none', $rahgozar, $fid);
                    if (!$response->success) {
                        if (strstr($response->msg ?? '', 'Duplicate email') || isPanelDuplicateEmailError($response->msg ?? '')) {
                            $remark .= RandomString();
                        } elseif (strstr($response->msg ?? '', 'Port already exists')) {
                            $port = rand(1111, 65000);
                        }
                        $response = addUser($server_id, $uniqid, $protocol, $port, $expire_microdate, $remark, $volume, $netType, 'none', $rahgozar, $fid);
                    }
                }
            } else {
                $response = addInboundAccount($server_id, $uniqid, $inbound_id, $expire_microdate, $remark, $volume, $limitip, null, $fid);
            }

            if (is_null($response) || $response === 'inbound not Found' || (is_object($response) && !$response->success)) {
                $err = is_object($response) ? (string) ($response->msg ?? '') : 'connection failed';
                self::handlePanelFailure($payHash, $err, $serverInfo);
                return;
            }

            $remark = syncRemarkFromPanelResponse($response, $remark);

            if (isUserApiPanelType($serverType)) {
                $delivery = deliverUserApiPanelPurchaseToCustomer(
                    $uid,
                    $server_id,
                    $serverType,
                    $remark,
                    $response,
                    $protocol,
                    $volume,
                    $days,
                    json_encode(['inline_keyboard' => [[['text' => $buttonValues['back_to_main'], 'callback_data' => 'mainMenu']]]])
                );
                $token = $delivery['token'];
                $uniqid = $delivery['uniqid'];
                $vray_link = $delivery['vray_link'];
            } else {
                $delivery = prepareXuiOrderDelivery($server_id, $remark, $fid, $inbound_id);
                $token = $delivery['token'];
                $uniqid = $delivery['uniqid'];
                $vray_link = $delivery['vray_link'];
            }

            $agent_bought = (int) ($payInfo['agent_bought'] ?? 0);
            $stmt = $connection->prepare(
                "INSERT INTO `orders_list`
                (`userid`, `token`, `transid`, `fileid`, `server_id`, `inbound_id`, `remark`, `uuid`, `protocol`, `expire_date`, `link`, `amount`, `status`, `date`, `notif`, `rahgozar`, `agent_bought`)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,1, ?, 0, ?, ?)"
            );
            $stmt->bind_param(
                'sssiiisssisiiii',
                $uid,
                $token,
                $payHash,
                $fid,
                $server_id,
                $inbound_id,
                $remark,
                $uniqid,
                $protocol,
                $expire_date,
                $vray_link,
                $eachPrice,
                $date,
                $rahgozar,
                $agent_bought
            );
            $stmt->execute();
            $stmt->close();

            if (!isUserApiPanelType($serverType)) {
                deliverXuiPanelOrderToCustomer(
                    $uid,
                    $server_id,
                    $remark,
                    json_encode(['inline_keyboard' => [[['text' => $buttonValues['back_to_main'], 'callback_data' => 'mainMenu']]]]),
                    $fid,
                    $inbound_id,
                    $delivery
                );
            }
        }

        $adminChat = !empty($payInfo['chat_id']) ? $payInfo['chat_id'] : $admin;
        sendMessage(
            str_replace(['REMARK', 'VOLUME', 'DAYS'], [$remark, $volume, $days], $mainValues['sent_config_to_user']),
            null,
            'HTML',
            $adminChat
        );

        if (!planHasInbounds($file_detail)) {
            $stmt = $connection->prepare('UPDATE `server_info` SET `ucount` = `ucount` - ? WHERE `id`=?');
            $stmt->bind_param('ii', $accountCount, $server_id);
            $stmt->execute();
            $stmt->close();
        } else {
            $stmt = $connection->prepare('UPDATE `server_plans` SET `acount` = `acount` - ? WHERE id=?');
            $stmt->bind_param('ii', $accountCount, $fid);
            $stmt->execute();
            $stmt->close();
        }

        markPayApproved($payHash);
        self::markAdminMessageDone($payInfo);
        self::notifyInviteAndAdmin($payInfo, $file_detail, $remark, $price, $uid);
    }

    /** @param array<string, mixed> $payInfo */
    private static function fulfillRenewPay(array $payInfo): void
    {
        global $connection, $admin, $mainValues;

        $payHash = (string) $payInfo['hash_id'];
        $uid = (int) $payInfo['user_id'];
        $fid = (int) $payInfo['plan_id'];
        $price = (int) $payInfo['price'];
        $time = time();

        $stmt = $connection->prepare('SELECT * FROM `server_plans` WHERE `id`=?');
        $stmt->bind_param('i', $fid);
        $stmt->execute();
        $file_detail = $stmt->get_result()->fetch_assoc();
        $stmt->close();

        $days = (int) ($file_detail['days'] ?? 0);
        $volume = (int) ($file_detail['volume'] ?? 0);
        $configInfo = json_decode((string) ($payInfo['description'] ?? ''), true) ?: [];
        $uuid = (string) ($configInfo['uuid'] ?? '');
        $remark = (string) ($configInfo['remark'] ?? '');
        $isUserApi = !empty($configInfo['marzban']) || !empty($configInfo['pasarguard']);
        $server_id = (int) ($file_detail['server_id'] ?? 0);
        $inbound_id = (int) $payInfo['volume'];

        if ($isUserApi) {
            $response = editUserApiPanelConfig($server_id, ['remark' => $remark, 'days' => $days, 'volume' => $volume]);
        } elseif ($inbound_id > 0) {
            $response = editClientTraffic($server_id, $inbound_id, $uuid, $volume, $days, 'renew');
        } else {
            $response = editInboundTraffic($server_id, $uuid, $volume, $days, 'renew');
        }

        if (is_null($response)) {
            self::handlePanelFailure($payHash, 'connection failed');
            return;
        }

        $stmt = $connection->prepare('INSERT INTO `increase_order` VALUES (NULL, ?, ?, ?, ?, ?, ?)');
        $stmt->bind_param('iiisii', $uid, $server_id, $inbound_id, $remark, $price, $time);
        $stmt->execute();
        $stmt->close();

        sendMessage(str_replace(['REMARK', 'VOLUME', 'DAYS'], [$remark, $volume, $days], $mainValues['renewed_config_to_user']), getMainKeys(), null, null, $uid);
        sendMessage("✅سرویس $remark با موفقیت تمدید شد", null, null, $uid);
        markPayApproved($payHash);
        self::markAdminMessageDone($payInfo);
    }

    /** @param array<string, mixed> $payInfo */
    private static function markAdminMessageDone(array $payInfo): void
    {
        if (empty($payInfo['message_id']) || empty($payInfo['chat_id'])) {
            return;
        }
        editKeys(
            json_encode(['inline_keyboard' => [[['text' => '✅', 'callback_data' => 'wizwizch']]]]),
            (int) $payInfo['message_id'],
            $payInfo['chat_id']
        );
    }

    /** @param array<string, mixed> $payInfo @param array<string, mixed> $file_detail */
    private static function notifyInviteAndAdmin(array $payInfo, array $file_detail, string $remark, int $price, int $uid): void
    {
        global $connection, $admin, $from_id, $mainValues;

        $filename = (string) ($file_detail['title'] ?? '');
        $stmt = $connection->prepare('SELECT * FROM `users` WHERE `userid`=?');
        $stmt->bind_param('i', $uid);
        $stmt->execute();
        $user_detail = $stmt->get_result()->fetch_assoc();
        $stmt->close();
        if (!$user_detail) {
            return;
        }

        ReferralService::onReferralPurchase((int) $uid);

        if ($admin != $from_id) {
            $msg = str_replace(
                ['USER-ID', 'USERNAME', 'NAME', 'PRICE', 'REMARK', 'FILENAME'],
                [$uid, $user_detail['username'] ?? '', $user_detail['name'] ?? '', $price, $remark, $filename],
                $mainValues['invite_buy_new_account']
            );
            sendMessage($msg, null, null, $admin);
        }
    }
}
