<?php
/**
 * OpenVPN | L2TP MiniApp API - Configuration
 * Uses centralized config from src/config.php
 */
header('Content-Type: application/json; charset=utf-8');

// Load centralized config
require_once __DIR__ . '/../../../src/config.php';

// Create PDO connection (using the centralized db() function)
$pdo = db();


/**
 * Runtime schema repair for cPanel/shared hosting.
 * This prevents Mini App "Network error" when database.sql was partially imported
 * or when an older database is used.
 */
function gemacc_column_exists(PDO $pdo, string $table, string $column): bool {
    try {
        $st = $pdo->prepare("SHOW COLUMNS FROM `$table` LIKE ?");
        $st->execute([$column]);
        return (bool)$st->fetch();
    } catch (Throwable $e) { return false; }
}
function gemacc_exec_silent(PDO $pdo, string $sql): void { try { $pdo->exec($sql); } catch (Throwable $e) { error_log('[SCHEMA] '.$e->getMessage().' SQL='.$sql); } }
function gemacc_ensure_runtime_schema(PDO $pdo): void {
    gemacc_exec_silent($pdo, "CREATE TABLE IF NOT EXISTS plan_categories (id INT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, months INT NOT NULL DEFAULT 1, is_active TINYINT(1) NOT NULL DEFAULT 1, display_order INT NOT NULL DEFAULT 0, sort_order INT NOT NULL DEFAULT 0, deleted_at DATETIME NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_active_order (is_active, display_order), INDEX idx_months (months), INDEX idx_deleted (deleted_at)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    if (!gemacc_column_exists($pdo, 'plan_categories', 'deleted_at')) gemacc_exec_silent($pdo, "ALTER TABLE plan_categories ADD COLUMN deleted_at DATETIME NULL");
    if (!gemacc_column_exists($pdo, 'plan_categories', 'sort_order')) gemacc_exec_silent($pdo, "ALTER TABLE plan_categories ADD COLUMN sort_order INT NOT NULL DEFAULT 0");
    // مهم: هیچ دسته پلن پیش‌فرضی اینجا ساخته نمی‌شود. دسته‌ها فقط از پنل مدیریت ساخته می‌شوند.

    gemacc_exec_silent($pdo, "ALTER TABLE plans MODIFY duration VARCHAR(20) NOT NULL DEFAULT '1M'");
    gemacc_exec_silent($pdo, "ALTER TABLE orders MODIFY duration VARCHAR(20) NOT NULL DEFAULT '1M'");
    gemacc_exec_silent($pdo, "ALTER TABLE credentials MODIFY duration VARCHAR(20) NOT NULL DEFAULT '1M'");
    gemacc_exec_silent($pdo, "ALTER TABLE credentials MODIFY sellable_until DATETIME NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE orders MODIFY status VARCHAR(30) NOT NULL DEFAULT 'PENDING'");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments MODIFY status VARCHAR(30) DEFAULT 'PENDING'");
    gemacc_exec_silent($pdo, "ALTER TABLE waiting_queue MODIFY duration VARCHAR(20) NOT NULL DEFAULT '1M'");
    gemacc_exec_silent($pdo, "ALTER TABLE orders MODIFY payment_method VARCHAR(30) NOT NULL DEFAULT 'CARD'");
    gemacc_exec_silent($pdo, "ALTER TABLE discount_codes ADD COLUMN payment_scope VARCHAR(20) NOT NULL DEFAULT 'all'");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN processed_at DATETIME DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN paid_at DATETIME DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE wallet_transactions ADD COLUMN crypto_payment_id BIGINT UNSIGNED DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE wallet_transactions ADD COLUMN receipt_text TEXT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE wallet_transactions ADD COLUMN receipt_file_id VARCHAR(255) NULL");

    gemacc_exec_silent($pdo, "ALTER TABLE users ADD COLUMN is_blocked TINYINT(1) NOT NULL DEFAULT 0");
    gemacc_exec_silent($pdo, "ALTER TABLE users ADD COLUMN blocked_reason TEXT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE users ADD COLUMN blocked_at DATETIME NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE users ADD COLUMN fake_receipt_warnings INT NOT NULL DEFAULT 0");
    gemacc_exec_silent($pdo, "ALTER TABLE users ADD COLUMN last_fake_receipt_warning_at DATETIME NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE orders ADD COLUMN fake_receipt_warning_at DATETIME NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE orders ADD COLUMN fake_receipt_warning_by BIGINT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE orders ADD COLUMN fake_receipt_warning_no INT NOT NULL DEFAULT 0");
    gemacc_exec_silent($pdo, "CREATE TABLE IF NOT EXISTS fake_receipt_warnings (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, order_id BIGINT NOT NULL, admin_id BIGINT NULL, warning_number INT NOT NULL DEFAULT 1, user_blocked TINYINT(1) NOT NULL DEFAULT 0, message_sent TINYINT(1) NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_created (user_id, created_at), KEY idx_order (order_id), KEY idx_admin (admin_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    gemacc_exec_silent($pdo, "ALTER TABLE credentials ADD COLUMN traffic_type VARCHAR(20) NOT NULL DEFAULT 'unlimited'");
    gemacc_exec_silent($pdo, "ALTER TABLE vpn_accounts ADD COLUMN traffic_type VARCHAR(20) NOT NULL DEFAULT 'unlimited'");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN receipt_file_id VARCHAR(255) NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN receipt_text TEXT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN processed_by BIGINT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN processed_at DATETIME NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN wallet_transaction_id INT UNSIGNED DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN payment_type VARCHAR(30) NOT NULL DEFAULT 'order'");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN provider_order_id VARCHAR(255) DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN raw_request LONGTEXT DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN paid_at DATETIME DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN crypto_currency VARCHAR(20) DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN network VARCHAR(50) DEFAULT NULL");
    gemacc_exec_silent($pdo, "ALTER TABLE crypto_payments ADD COLUMN wallet_address TEXT DEFAULT NULL");

    $cols=['plans'=>['category_id'=>'INT NULL AFTER id','deleted_at'=>'DATETIME NULL','user_count'=>'INT NOT NULL DEFAULT 1','months'=>'INT NOT NULL DEFAULT 1'],'credentials'=>['category_id'=>'INT NULL AFTER id','assigned_user_id'=>'BIGINT NULL AFTER used_seats','assigned_order_id'=>'INT NULL AFTER assigned_user_id','first_connected_at'=>'TIMESTAMP NULL AFTER assigned_order_id','expires_at'=>'TIMESTAMP NULL AFTER first_connected_at','protocol'=>"VARCHAR(30) NOT NULL DEFAULT 'OpenVPN/L2TP' AFTER duration"]];
    foreach($cols as $table=>$map){ foreach($map as $col=>$def){ if(!gemacc_column_exists($pdo,$table,$col)) gemacc_exec_silent($pdo,"ALTER TABLE `$table` ADD COLUMN `$col` $def"); } }
    gemacc_exec_silent($pdo, "ALTER TABLE credentials ALTER max_seats SET DEFAULT 1");

    gemacc_exec_silent($pdo, "CREATE TABLE IF NOT EXISTS vpn_connections (id BIGINT AUTO_INCREMENT PRIMARY KEY, credential_id INT NOT NULL, user_id BIGINT NULL, username VARCHAR(255) NOT NULL, connected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, source_ip VARCHAR(64) DEFAULT NULL, raw_payload JSON DEFAULT NULL, INDEX idx_username (username), INDEX idx_credential (credential_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    gemacc_exec_silent($pdo, "CREATE TABLE IF NOT EXISTS first_connections (id BIGINT AUTO_INCREMENT PRIMARY KEY, credential_id INT NOT NULL UNIQUE, user_id BIGINT NULL, username VARCHAR(255) NOT NULL, first_connected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP NULL, INDEX idx_user (user_id), INDEX idx_username (username)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    gemacc_exec_silent($pdo, "CREATE TABLE IF NOT EXISTS crypto_payments (id BIGINT AUTO_INCREMENT PRIMARY KEY, order_id INT NULL, user_id BIGINT NOT NULL, amount INT NOT NULL DEFAULT 0, currency VARCHAR(20) DEFAULT 'IRR', provider VARCHAR(50) DEFAULT 'SwapWallet', provider_invoice_id VARCHAR(255) DEFAULT NULL, payment_url TEXT DEFAULT NULL, status VARCHAR(30) DEFAULT 'PENDING', raw_response LONGTEXT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_order (order_id), INDEX idx_user_status (user_id,status)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    gemacc_exec_silent($pdo, "INSERT INTO settings(key_name,value) VALUES ('express_month_purchase_total','500000'),('express_month_purchase_seats','10'),('express_year_purchase_per_user','250000'),('openvpn_purchase_price_per_user','180000'),('windscribe_month_purchase_total','1500000'),('windscribe_month_purchase_seats','10'),('windscribe_year_purchase_total','5000000'),('windscribe_year_purchase_seats','15'),('plisio_enabled','0'),('plisio_api_key',''),('plisio_usd_rate_toman','60000'),('plisio_currencies','USDT_BSC,TRX,BTC,LTC'),('plisio_allow_all_currencies','1'),('manual_crypto_enabled','1'),('manual_crypto_usdt_address',''),('manual_crypto_trx_address',''),('manual_crypto_usdt_network','BEP20 / BNB Smart Chain'),('manual_crypto_trx_network','TRON'),('manual_crypto_receipt_help','بعد از واریز، عکس رسید یا هش تراکنش را ارسال کنید.'),('manual_crypto_usdt_irt_rate','0'),('manual_crypto_trx_irt_rate','0'),('manual_crypto_live_rates_enabled','0'),('swapwallet_api_key',''),('swapwallet_payment_username',''),('swapwallet_enabled','0'),('swapwallet_invoice_endpoint',''),('swapwallet_base_url','https://swapwallet.app/api'),('swapwallet_default_token','USDT'),('swapwallet_default_network','TRON'),('card_payment_enabled','1'),('openvpn_config_url','https://laksho1.com'),('l2tp_server','s8.laksho1.com'),('signup_bonus_toman','15000'),('stars_enabled','0'),('stars_rate_toman','7000') ON DUPLICATE KEY UPDATE value=IF(key_name IN ('card_payment_enabled','openvpn_config_url','l2tp_server','swapwallet_base_url'),VALUES(value),value)");
}
$__schemaRepair = function_exists('env') ? (string)env('GEMACC_RUNTIME_SCHEMA_REPAIR', '0') : (string)(getenv('GEMACC_RUNTIME_SCHEMA_REPAIR') ?: '0');
if (in_array(strtolower(trim($__schemaRepair)), ['1','true','yes','on'], true)) {
    gemacc_ensure_runtime_schema($pdo);
}


/**
 * Verify Telegram Mini App initData when provided by the frontend.
 * This does not break old production endpoints that still pass user_id,
 * but it lets secure/admin calls rely on the real Telegram user id.
 */
function miniapp_verify_init_data_optional(): ?array {
    $initData = $_SERVER['HTTP_X_TELEGRAM_INIT_DATA'] ?? $_POST['initData'] ?? $_GET['initData'] ?? '';
    $initData = (string)$initData;
    if ($initData === '') return null;

    parse_str($initData, $data);
    if (empty($data['hash']) || empty($data['user'])) return null;
    $receivedHash = (string)$data['hash'];
    unset($data['hash']);
    ksort($data);
    $pairs = [];
    foreach ($data as $k => $v) $pairs[] = $k . '=' . $v;
    $checkString = implode("\n", $pairs);

    if (!defined('BOT_TOKEN') || trim((string)BOT_TOKEN) === '') return null;
    $secretKey = hash_hmac('sha256', BOT_TOKEN, 'WebAppData', true);
    $calculatedHash = hash_hmac('sha256', $checkString, $secretKey);
    if (!hash_equals($calculatedHash, $receivedHash)) return null;

    $authDate = isset($data['auth_date']) ? (int)$data['auth_date'] : 0;
    if ($authDate > 0 && time() - $authDate > 86400) return null;

    $user = json_decode((string)$data['user'], true);
    return (is_array($user) && !empty($user['id'])) ? $user : null;
}


$__miniAppAuthUser = miniapp_verify_init_data_optional();
if (is_array($__miniAppAuthUser) && !empty($__miniAppAuthUser['id'])) {
    // Verified Telegram identity: force user_id everywhere so endpoints cannot be
    // tricked into operating on another account via a forged body field.
    $_GET['user_id'] = $_POST['user_id'] = $_REQUEST['user_id'] = (string)(int)$__miniAppAuthUser['id'];
}

/**
 * ────────────────────────────────────────────────────────────────────────────
 * v27.52 SECURITY FIX (Bug #1): mandatory initData for user endpoints.
 *
 * Previously, if a request did NOT send a valid Telegram initData, the API
 * silently trusted whatever user_id was in the request body/query. That allowed
 * anyone to read or act on any account by calling the API directly.
 *
 * Now: when initData is absent/invalid, any endpoint that relies on a
 * client-supplied user_id is rejected — UNLESS it is on the public allowlist
 * below (server-to-server webhooks / health checks that legitimately have no
 * Telegram user). Endpoints that already require initData (admin, Stars) are
 * unaffected.
 *
 * Set MINIAPP_REQUIRE_INITDATA=0 in .env ONLY for temporary debugging.
 * ────────────────────────────────────────────────────────────────────────────
 */
function gemacc_env_flag(string $key, bool $default): bool {
    $raw = null;
    if (function_exists('env')) $raw = env($key, null);
    if ($raw === null && class_exists('App\Env')) $raw = App\Env::get($key, null);
    if ($raw === null) $raw = getenv($key);
    if ($raw === false || $raw === null || $raw === '') return $default;
    return in_array(strtolower(trim((string)$raw)), ['1','true','yes','on'], true);
}

function gemacc_current_script_basename(): string {
    $self = $_SERVER['SCRIPT_NAME'] ?? ($_SERVER['PHP_SELF'] ?? '');
    return strtolower(basename((string)$self));
}

/**
 * Endpoints that are allowed to run WITHOUT a verified Telegram initData,
 * because they are called by payment providers / monitoring, not by the user's
 * browser. These must do their own provider-signature verification (they do).
 */
function gemacc_miniapp_public_endpoints(): array {
    return [
        'health.php',
        'swapwallet_webhook.php',
        'swapwallet_wallet_webhook.php',
        'plisio_webhook.php',
        'config.php', // never called directly; included by others
    ];
}

if (gemacc_env_flag('MINIAPP_REQUIRE_INITDATA', true)) {
    $__verified = is_array($__miniAppAuthUser) && !empty($__miniAppAuthUser['id']);
    $__script   = gemacc_current_script_basename();
    $__isPublic = in_array($__script, gemacc_miniapp_public_endpoints(), true);

    if (!$__verified && !$__isPublic) {
        // If the caller supplied a user_id without proving identity, refuse.
        $__claimedUid = $_GET['user_id'] ?? $_POST['user_id'] ?? $_REQUEST['user_id'] ?? null;
        if ($__claimedUid !== null && $__claimedUid !== '') {
            http_response_code(401);
            echo json_encode([
                'ok'    => false,
                'error' => 'احراز هویت نامعتبر است. لطفاً مینی‌اپ را از داخل تلگرام یا اپ مدیریت اندروید باز کنید.',
                'code'  => 'INITDATA_REQUIRED',
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        // No identity and no user_id: also strip any forged values so endpoints
        // that read user_id get nothing and fail their own validation cleanly.
        unset($_GET['user_id'], $_POST['user_id'], $_REQUEST['user_id']);
    }
}


/**
 * Strict admin guard for Mini App admin APIs.
 * Admin endpoints must never trust a client-supplied user_id. They require a
 * valid Telegram WebApp initData and then check that Telegram user id against
 * ADMIN_IDS/users.is_admin. Normal user endpoints may still use the legacy
 * optional auth flow above for backward compatibility.
 */
function miniapp_authenticated_user_id(): int {
    global $__miniAppAuthUser;
    return (is_array($__miniAppAuthUser) && !empty($__miniAppAuthUser['id'])) ? (int)$__miniAppAuthUser['id'] : 0;
}
function miniapp_require_admin(): int {
    $uid = miniapp_authenticated_user_id();
    if (!$uid || !isAdmin($uid)) {
        http_response_code(403);
        echo json_encode(['ok' => false, 'error' => 'Admin access required'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }
    return $uid;
}

/**
 * Ensure user exists in database
 */
function ensureUser(array $u): void {
    global $pdo;
    $stmt = $pdo->prepare("
        INSERT INTO users (id, username, first_name, last_name, is_admin)
        VALUES (:id, :un, :fn, :ln, :is_admin)
        ON DUPLICATE KEY UPDATE 
            username = VALUES(username), 
            first_name = VALUES(first_name), 
            last_name = VALUES(last_name), 
            is_admin = IF(users.is_admin = 1 OR VALUES(is_admin) = 1, 1, 0)
    ");
    $stmt->execute([
        ':id'       => $u['id'],
        ':un'       => $u['username'] ?? null,
        ':fn'       => $u['first_name'] ?? null,
        ':ln'       => $u['last_name'] ?? null,
        ':is_admin' => isAdmin($u['id']) ? 1 : 0,
    ]);
}

// Note: getSetting() and setSetting() are already defined in src/config.php
// They are available globally and should be used directly
