<?php
/**
 * buy_with_stars.php  (v27.53)
 * ─────────────────────────────────────────────────────────────────────────────
 * Telegram Stars (XTR) payment for BOTH:
 *   • mode = "order"  (default) → buy any plan/service
 *   • mode = "wallet"           → top up the user's wallet balance
 *
 * Flow:
 *   1. Frontend calls this endpoint:
 *        order:  {mode:"order", plan_id, discount_code?, request_id}
 *        wallet: {mode:"wallet", amount, request_id}
 *   2. We ask Telegram for an invoice link via createInvoiceLink (currency="XTR")
 *      FIRST, and only persist a PENDING order / wallet tx once Telegram has
 *      returned a valid link. If Telegram errors, nothing is written.
 *   3. Frontend opens the link with Telegram.WebApp.openInvoice(...).
 *   4. Telegram sends pre_checkout_query  -> bot answers OK   (src/legacy/bot.php)
 *   5. Telegram sends successful_payment  -> bot delivers/credits (src/legacy/bot.php)
 *
 * IMPORTANT (bug #2 fix): createInvoiceLink "prices" must be a NATIVE PHP array.
 * tg() json_encodes the whole payload, so passing json_encode() here would
 * double-encode it and Telegram rejects the request.
 *
 * IMPORTANT (bug #3 fix): the order is delivered ONLY on successful_payment, and
 * delivery is idempotent + automatic (auto-allocate, or queue if out of stock) —
 * no manual admin confirmation. A failed/cancelled payment leaves the order as
 * PENDING/unpaid and it is never delivered.
 *
 * Price conversion: final Toman price -> Stars using setting `stars_rate_toman`
 * (Toman per 1 Star). Rounded UP, min 1, so we never undercharge.
 * ─────────────────────────────────────────────────────────────────────────────
 */
require_once 'config.php';

if (function_exists('getSetting') && getSetting('sales_enabled', '1') !== '1') {
    echo json_encode(['ok' => false, 'error' => 'فروش موقتاً متوقف است. لطفاً بعداً تلاش کنید.'], JSON_UNESCAPED_UNICODE);
    exit;
}

require_once '_vpn_helpers.php';
require_once 'discount_engine.php';
require_once 'plan_price_helper.php';
header('Content-Type: application/json; charset=utf-8');

function stars_json($data, int $code = 200) {
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') stars_json(['ok' => false, 'error' => 'POST required']);

// Stars must be enabled by the admin.
if (function_exists('getSetting') && getSetting('stars_enabled', '0') !== '1') {
    stars_json(['ok' => false, 'error' => 'پرداخت با استارز تلگرام فعلاً غیرفعال است.']);
}

$input        = json_decode(file_get_contents('php://input'), true) ?: $_POST;
$mode         = strtolower(trim((string)($input['mode'] ?? 'order')));
$userId       = (int)($input['user_id'] ?? 0);
$requestId    = trim((string)($input['request_id'] ?? ''));

if (!$userId) stars_json(['ok' => false, 'error' => 'user_id required']);
try { if (function_exists('gemaccStarsEnsureAutoCancelColumns')) gemaccStarsEnsureAutoCancelColumns($pdo); } catch (Throwable $e) { error_log('[stars_columns] '.$e->getMessage()); }
try { if (function_exists('gemaccStarsAutoCancelExpired')) gemaccStarsAutoCancelExpired($pdo, 25); } catch (Throwable $e) { error_log('[stars_auto_cancel_buy] '.$e->getMessage()); }

// Blocked-user guard.
try {
    $st = $pdo->prepare("SELECT is_blocked FROM users WHERE id=? LIMIT 1");
    $st->execute([$userId]);
    if ((int)$st->fetchColumn() === 1) stars_json(['ok' => false, 'error' => 'حساب شما مسدود است. با پشتیبانی تماس بگیرید.']);
} catch (Throwable $e) {}

/** Convert a Toman amount to a whole number of Stars (rounded up, min 1). */
function stars_amount_from_toman(int $toman): int {
    $rate = (int)(function_exists('getSetting') ? getSetting('stars_rate_toman', '7000') : '7000');
    if ($rate < 1) $rate = 7000;
    if ($toman <= 0) return 1; // Telegram requires amount >= 1
    return max(1, (int)ceil($toman / $rate));
}

function stars_rate_toman(): int {
    $rate = (int)(function_exists('getSetting') ? getSetting('stars_rate_toman', '7000') : '7000');
    return $rate < 1 ? 7000 : $rate;
}
function stars_service_label(array $row): string {
    $custom = trim((string)($row['category_service_name'] ?? $row['service_name'] ?? ''));
    if ($custom !== '') return $custom;
    $raw = strtolower((string)($row['protocol'] ?? $row['category_service_type'] ?? 'openvpn_l2tp'));
    $hint = strtolower((string)($row['title'] ?? '') . ' ' . (string)($row['category_name'] ?? ''));
    if ($raw === 'windscribe' || str_contains($hint, 'windscribe') || str_contains($hint, 'ویندسکرایب')) return 'Windscribe';
    if ($raw === 'expressvpn' || str_contains($hint, 'express') || str_contains($hint, 'اکسپرس')) return 'ExpressVPN';
    return 'OpenVPN | L2TP';
}
function stars_duration_label(array $row): string {
    $months = max(1, (int)($row['months'] ?? $row['category_months'] ?? 1));
    if ($months === 12) return 'یک‌ساله / ۱۲ ماهه';
    if ($months === 1) return 'یک‌ماهه';
    return $months . ' ماهه';
}
function stars_invoice_description(array $plan, int $finalPrice, int $starsAmount, int $requestedSeats): string {
    $rate = stars_rate_toman();
    $lines = [
        'خرید امن با استارز GEM ACC',
        'سرویس: ' . stars_service_label($plan),
        'مدت: ' . stars_duration_label($plan),
        'تعداد کاربر: ' . $requestedSeats,
        'قیمت: ' . number_format($finalPrice) . ' تومان',
        'تبدیل: هر ⭐ = ' . number_format($rate) . ' تومان',
        'پرداخت نهایی: ' . $starsAmount . '⭐',
    ];
    return mb_substr(implode("
", $lines), 0, 255, 'UTF-8');
}

/**
 * Ask Telegram for an invoice link. Returns the link string or throws.
 * NOTE: prices MUST be a native array (see bug #2 note above).
 */
function stars_create_invoice_link(string $title, string $description, string $payload, int $starsAmount): string {
    $resp = tg('createInvoiceLink', [
        'title'       => mb_substr($title, 0, 32),
        'description' => mb_substr($description, 0, 255),
        'payload'     => $payload,
        'currency'    => 'XTR',
        'prices'      => [['label' => 'پرداخت استارز', 'amount' => $starsAmount]], // native array — do NOT json_encode
    ]);
    $decoded = is_string($resp) ? json_decode($resp, true) : $resp;
    $link = is_array($decoded) ? ($decoded['result'] ?? null) : null;
    if (!$link) {
        $desc = is_array($decoded) ? ($decoded['description'] ?? '') : '';
        throw new RuntimeException('ساخت لینک پرداخت استارز ناموفق بود' . ($desc ? " ({$desc})" : '') . '. لطفاً بعداً تلاش کنید.');
    }
    return (string)$link;
}

// ════════════════════════════════════════════════════════════════════════════
// MODE: WALLET TOP-UP  (bug #4)
// ════════════════════════════════════════════════════════════════════════════
if ($mode === 'wallet') {
    $amount = (int)($input['amount'] ?? 0);
    if ($amount < 10000) stars_json(['ok' => false, 'error' => 'حداقل مبلغ شارژ ۱۰,۰۰۰ تومان است.']);
    if ($amount > 500000000) stars_json(['ok' => false, 'error' => 'مبلغ شارژ بیش از حد مجاز است.']);

    try {
        // Idempotency: a repeated request_id returns the same invoice.
        $dupe = gemacc_idempotency_begin($pdo, $userId, $requestId, 'stars_wallet');
        if ($dupe) {
            $cached = json_decode((string)($dupe['response_json'] ?? ''), true);
            if (is_array($cached) && !empty($cached)) stars_json($cached);
            stars_json(['ok' => false, 'error' => 'این درخواست در حال پردازش است. چند لحظه صبر کنید.']);
        }

        $starsAmount = stars_amount_from_toman($amount);

        // Create the invoice FIRST. If Telegram fails, we write nothing (bug #3).
        $payloadTmp = 'STARS_WALLET_PENDING_' . $userId;
        // We need the wallet_transactions id inside the payload, so create the
        // PENDING tx in a transaction, build the link, then commit. If link
        // creation fails we roll back and nothing remains.
        $pdo->beginTransaction();
        $meta = json_encode([
            'request_id' => $requestId,
            'amount'     => $amount,
            'stars'      => $starsAmount,
            'payment'    => 'stars',
            'kind'       => 'wallet_topup',
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $pdo->prepare("INSERT INTO wallet_transactions(user_id,amount,type,status,payment_method,currency,description,receipt_text) VALUES(?,?,'DEPOSIT','PENDING','stars','XTR',?,?)")
            ->execute([$userId, $amount, 'شارژ کیف پول با استارز تلگرام', $meta]);
        $txId = (int)$pdo->lastInsertId();

        $payload = 'STARS_WALLET_' . $txId;
        try {
            $invoiceLink = stars_create_invoice_link(
                'شارژ کیف پول',
                'افزایش موجودی کیف پول به مبلغ ' . number_format($amount) . ' تومان',
                $payload,
                $starsAmount
            );
        } catch (Throwable $e) {
            $pdo->rollBack();
            $response = ['ok' => false, 'error' => $e->getMessage()];
            if ($requestId !== '') { try { gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_wallet', null, $response); } catch (Throwable $ig) {} }
            stars_json($response);
        }

        $response = [
            'ok'           => true,
            'mode'         => 'wallet',
            'tx_id'        => $txId,
            'invoice_link' => $invoiceLink,
            'stars_amount' => $starsAmount,
            'amount'       => $amount,
            'message'      => 'لینک پرداخت استارز ساخته شد. پنجره پرداخت تلگرام را تأیید کنید.',
        ];
        gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_wallet', null, $response);
        $pdo->commit();
        stars_json($response);

    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        $response = ['ok' => false, 'error' => 'خطا: ' . $e->getMessage()];
        if ($requestId !== '') { try { gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_wallet', null, $response); } catch (Throwable $ig) {} }
        stars_json($response);
    }
}

// ════════════════════════════════════════════════════════════════════════════
// MODE: ORDER  (buy a plan)  — default
// ════════════════════════════════════════════════════════════════════════════
$planId       = (int)($input['plan_id'] ?? 0);
$discountCode = gemacc_normalize_discount_code($input['discount_code'] ?? '');

if (!$planId) stars_json(['ok' => false, 'error' => 'plan_id required']);

try {
    // Idempotency: a repeated request_id returns the same invoice instead of
    // creating a second order.
    $dupe = gemacc_idempotency_begin($pdo, $userId, $requestId, 'stars_order');
    if ($dupe) {
        $cached = json_decode((string)($dupe['response_json'] ?? ''), true);
        if (is_array($cached) && !empty($cached)) stars_json($cached);
        stars_json(['ok' => false, 'error' => 'این درخواست در حال پردازش است. چند لحظه صبر کنید.']);
    }

    $pdo->beginTransaction();

    $st = $pdo->prepare("SELECT p.*, pc.service_type AS category_service_type, pc.service_name AS category_service_name, pc.traffic_type AS category_traffic_type, pc.traffic_limit_gb AS category_traffic_limit_gb, pc.months AS category_months, pc.name AS category_name FROM plans p LEFT JOIN plan_categories pc ON pc.id=p.category_id WHERE p.id=? AND p.is_active=1 FOR UPDATE");
    $st->execute([$planId]);
    $plan = $st->fetch(PDO::FETCH_ASSOC);
    if (!$plan) throw new RuntimeException('پلن یافت نشد');

    $originalPrice = gemacc_plan_effective_sale_price($plan);
    $discount = ['valid' => false, 'discount_amount' => 0, 'final_price' => $originalPrice];
    if ($discountCode !== '') {
        $discount = gemacc_discount_preview($pdo, $userId, $discountCode, $originalPrice, $planId, 'stars');
        if (empty($discount['valid'])) throw new RuntimeException($discount['message'] ?? 'کد تخفیف نامعتبر است');
    }
    $finalPrice = max(0, (int)($discount['final_price'] ?? $originalPrice));
    $starsAmount = stars_amount_from_toman($finalPrice);

    $requestedSeats = max(1, (int)($plan['seats'] ?? $plan['user_count'] ?? 1));
    $trafficType    = $plan['traffic_type'] ?: ($plan['category_traffic_type'] ?? 'unlimited');
    $trafficLimitGb = $trafficType === 'limited' ? ($plan['traffic_limit_gb'] ?? $plan['category_traffic_limit_gb'] ?? null) : null;
    $rawSvc         = strtolower((string)($plan['protocol'] ?? $plan['category_service_type'] ?? 'openvpn_l2tp'));
    $serviceType    = in_array($rawSvc, ['windscribe', 'expressvpn', 'v2ray'], true) ? $rawSvc : 'openvpn_l2tp';
    $months         = max(1, (int)($plan['months'] ?? $plan['category_months'] ?? 1));

    $receiptMeta = json_encode([
        'request_id'      => $requestId,
        'original_price'  => $originalPrice,
        'discount_code'   => $discountCode,
        'discount_amount' => (int)($discount['discount_amount'] ?? 0),
        'final_price'     => $finalPrice,
        'stars_amount'    => $starsAmount,
        'stars_rate_toman'=> stars_rate_toman(),
        'service_label'   => stars_service_label($plan),
        'duration_label'  => stars_duration_label($plan),
        'payment'         => 'stars',
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    $autoCancelSeconds = function_exists('gemaccStarsPendingAutoCancelSeconds') ? gemaccStarsPendingAutoCancelSeconds() : 15;
    $st = $pdo->prepare("INSERT INTO orders(user_id,plan_id,category_id,plan_code,plan_title,service_type,duration,months,requested_seats,required_accounts,delivered_accounts,traffic_type,traffic_limit_gb,price,purchase_price,payment_method,payment_status,delivery_status,status,receipt_text,stars_expires_at) VALUES(?,?,?,?,?,?,?,?,?,?,0,?,?,?,?,'STARS','pending','pending','PENDING',?,DATE_ADD(NOW(), INTERVAL {$autoCancelSeconds} SECOND))");
    $st->execute([
        $userId, $planId, $plan['category_id'] ?? null, $plan['code'] ?? null,
        $plan['title'] ?? 'OpenVPN | L2TP', $serviceType, $plan['duration'], $months,
        $requestedSeats, $requestedSeats, $trafficType, $trafficLimitGb,
        $finalPrice, gemacc_plan_effective_purchase_price($plan), $receiptMeta,
    ]);
    $orderId = (int)$pdo->lastInsertId();

    if (function_exists('gemaccStarsEnqueueAutoCancel')) { gemaccStarsEnqueueAutoCancel($pdo, $orderId, $userId); }

    // Reserve the discount against this order (consumed on delivery).
    if (!empty($discount['valid'])) gemacc_discount_apply($pdo, $discount, $userId, $orderId);

    // Build the invoice. The payload lets the webhook find this exact order.
    // prices is a NATIVE array (bug #2 fix).
    $title       = 'خرید ' . ($plan['title'] ?? 'سرویس');
    $description = stars_invoice_description($plan, $finalPrice, $starsAmount, $requestedSeats);
    if (trim($description) === '') $description = 'سرویس دیجیتال GEM ACC';
    $payload = 'STARS_ORDER_' . $orderId;

    try {
        $invoiceLink = stars_create_invoice_link($title, $description, $payload, $starsAmount);
    } catch (Throwable $e) {
        // Telegram refused the invoice: roll back so NO orphan PENDING order
        // and NO reserved discount remain (bug #3).
        $pdo->rollBack();
        $response = ['ok' => false, 'error' => $e->getMessage()];
        if ($requestId !== '') { try { gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_order', null, $response); } catch (Throwable $ig) {} }
        stars_json($response);
    }

    $response = [
        'ok'              => true,
        'mode'            => 'order',
        'order_id'        => $orderId,
        'invoice_link'    => $invoiceLink,
        'stars_amount'    => $starsAmount,
        'stars_rate_toman'=> stars_rate_toman(),
        'service_label'   => stars_service_label($plan),
        'duration_label'  => stars_duration_label($plan),
        'requested_seats' => $requestedSeats,
        'expires_in'      => function_exists('gemaccStarsPendingAutoCancelSeconds') ? gemaccStarsPendingAutoCancelSeconds() : 15,
        'original_price'  => $originalPrice,
        'discount_amount' => (int)($discount['discount_amount'] ?? 0),
        'final_price'     => $finalPrice,
        'message'         => 'لینک پرداخت استارز ساخته شد. پنجره پرداخت تلگرام را تأیید کنید.',
    ];

    gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_order', $orderId, $response);
    $pdo->commit();
    stars_json($response);

} catch (Throwable $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();
    $response = ['ok' => false, 'error' => 'خطا: ' . $e->getMessage()];
    if ($requestId !== '') {
        try { gemacc_idempotency_finish($pdo, $userId, $requestId, 'stars_order', null, $response); } catch (Throwable $ig) {}
    }
    stars_json($response);
}
