<?php

declare(strict_types=1);

/*
|--------------------------------------------------------------------------
| Pengaturan error
|--------------------------------------------------------------------------
| Untuk produksi display_errors harus 0.
*/

ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
error_reporting(E_ALL);

/*
|--------------------------------------------------------------------------
| Session admin
|--------------------------------------------------------------------------
*/

ini_set('session.use_strict_mode', '1');
ini_set('session.use_only_cookies', '1');
ini_set('session.cookie_httponly', '1');

$isHttps = (
    (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
    (
        isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
        $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'
    )
);

session_name('ADMINPANELSESSID');

session_set_cookie_params([
    'lifetime' => 0,
    'path'     => '/',
    'domain'   => '',
    'secure'   => $isHttps,
    'httponly' => true,
    'samesite' => 'Lax',
]);

session_start();

/*
|--------------------------------------------------------------------------
| Wajib login admin
|--------------------------------------------------------------------------
*/



/*
|--------------------------------------------------------------------------
| Koneksi database
|--------------------------------------------------------------------------
*/

$databaseFile = dirname(__DIR__) . '/config/database.php';

if (!is_file($databaseFile)) {
    http_response_code(500);
    exit('File config/database.php tidak ditemukan.');
}

require_once $databaseFile;

if (!isset($pdo) || !($pdo instanceof PDO)) {
    http_response_code(500);
    exit('Koneksi PDO tidak tersedia.');
}

/*
|--------------------------------------------------------------------------
| Helper respons JSON
|--------------------------------------------------------------------------
*/

function nexusGameJsonResponse(array $data, int $httpCode = 200)
{
    http_response_code($httpCode);

    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    header('Cache-Control: no-store, no-cache, must-revalidate');

    echo json_encode(
        $data,
        JSON_PRETTY_PRINT |
        JSON_UNESCAPED_UNICODE |
        JSON_UNESCAPED_SLASHES
    );

    exit;
}

/*
|--------------------------------------------------------------------------
| Helper potong teks
|--------------------------------------------------------------------------
*/

function nexusGameCutText(string $value, int $maximum): string
{
    if (function_exists('mb_strlen')) {
        if (mb_strlen($value, 'UTF-8') > $maximum) {
            return mb_substr(
                $value,
                0,
                $maximum,
                'UTF-8'
            );
        }

        return $value;
    }

    if (strlen($value) > $maximum) {
        return substr($value, 0, $maximum);
    }

    return $value;
}

/*
|--------------------------------------------------------------------------
| CSRF
|--------------------------------------------------------------------------
*/

if (
    empty($_SESSION['csrf_sync_games_nexus']) ||
    !is_string($_SESSION['csrf_sync_games_nexus'])
) {
    $_SESSION['csrf_sync_games_nexus'] = bin2hex(
        random_bytes(32)
    );
}

/*
|--------------------------------------------------------------------------
| POST: sinkronisasi satu provider
|--------------------------------------------------------------------------
*/

if (
    isset($_SERVER['REQUEST_METHOD']) &&
    $_SERVER['REQUEST_METHOD'] === 'POST'
) {
    /*
    |--------------------------------------------------------------------------
    | Validasi CSRF
    |--------------------------------------------------------------------------
    */

    $csrfInput = isset($_POST['csrf_token'])
        ? (string) $_POST['csrf_token']
        : '';

    $csrfSession = isset($_SESSION['csrf_sync_games_nexus'])
        ? (string) $_SESSION['csrf_sync_games_nexus']
        : '';

    if (
        $csrfInput === '' ||
        $csrfSession === '' ||
        !hash_equals($csrfSession, $csrfInput)
    ) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'CSRF token tidak valid atau kedaluwarsa.',
        ], 419);
    }

    /*
    |--------------------------------------------------------------------------
    | Validasi provider code
    |--------------------------------------------------------------------------
    */

    $providerCode = isset($_POST['provider_code'])
        ? strtoupper(trim((string) $_POST['provider_code']))
        : '';

    if (
        $providerCode === '' ||
        preg_match(
            '/^[A-Z0-9_-]{1,50}$/',
            $providerCode
        ) !== 1
    ) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Provider code tidak valid.',
        ], 422);
    }

    /*
    |--------------------------------------------------------------------------
    | Ambil provider dari database
    |--------------------------------------------------------------------------
    */

    try {
        $providerStatement = $pdo->prepare("
            SELECT
                id,
                provider_code,
                provider_name,
                provider_status
            FROM providers_nexus
            WHERE provider_code = :provider_code
            LIMIT 1
        ");

        $providerStatement->execute([
            'provider_code' => $providerCode,
        ]);

        $providerData = $providerStatement->fetch(
            PDO::FETCH_ASSOC
        );
    } catch (Throwable $e) {
        error_log(
            'Nexus provider lookup error: ' .
            $e->getMessage()
        );

        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Gagal membaca provider Nexus.',
        ], 500);
    }

    if (!$providerData) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Provider Nexus tidak ditemukan.',
        ], 404);
    }

    $providerId = (int) $providerData['id'];
    $providerName = (string) $providerData['provider_name'];

    /*
    |--------------------------------------------------------------------------
    | Konfigurasi Nexus
    |--------------------------------------------------------------------------
    |
    | Gunakan konfigurasi yang sama dengan sync_provider_nexus.php.
    */

    $apiUrl = trim(
        getenv('NEXUS_API_URL')
            ?: 'https://api.nexusggr.com'
    );

    $agentCode = trim(
        getenv('NEXUS_AGENT_CODE')
            ?: 'uno15'
    );

    $agentToken = trim(
        getenv('NEXUS_AGENT_TOKEN')
            ?: '9611e842cbd19494e0f080e2f4d4af03'
    );

    /*
    |--------------------------------------------------------------------------
    | Validasi konfigurasi
    |--------------------------------------------------------------------------
    */

    if (
        $apiUrl === '' ||
        $apiUrl === 'https://API_SERVER_ANDA' ||
        filter_var(
            $apiUrl,
            FILTER_VALIDATE_URL
        ) === false
    ) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'NEXUS_API_URL belum dikonfigurasi.',
        ], 500);
    }

    $apiScheme = strtolower(
        (string) parse_url(
            $apiUrl,
            PHP_URL_SCHEME
        )
    );

    if ($apiScheme !== 'https') {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Endpoint Nexus wajib menggunakan HTTPS.',
        ], 500);
    }

    if (
        $agentCode === '' ||
        $agentCode === 'MASUKKAN_AGENT_CODE'
    ) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'NEXUS_AGENT_CODE belum dikonfigurasi.',
        ], 500);
    }

    if (
        $agentToken === '' ||
        $agentToken === 'MASUKKAN_AGENT_TOKEN'
    ) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'NEXUS_AGENT_TOKEN belum dikonfigurasi.',
        ], 500);
    }

    /*
    |--------------------------------------------------------------------------
    | Payload game_list
    |--------------------------------------------------------------------------
    */

    $payload = [
        'method'        => 'game_list',
        'agent_code'    => $agentCode,
        'agent_token'   => $agentToken,
        'provider_code' => $providerCode,
    ];

    $jsonPayload = json_encode(
        $payload,
        JSON_UNESCAPED_SLASHES |
        JSON_UNESCAPED_UNICODE
    );

    if ($jsonPayload === false) {
        error_log(
            'Nexus game_list JSON error: ' .
            json_last_error_msg()
        );

        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Gagal membuat payload Nexus.',
        ], 500);
    }

    /*
    |--------------------------------------------------------------------------
    | Request API Nexus
    |--------------------------------------------------------------------------
    */

    $ch = curl_init();

    if ($ch === false) {
        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Gagal menginisialisasi cURL.',
        ], 500);
    }

    curl_setopt_array($ch, [
        CURLOPT_URL            => $apiUrl,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $jsonPayload,
        CURLOPT_RETURNTRANSFER => true,

        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT        => 60,

        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,

        CURLOPT_IPRESOLVE      => CURL_IPRESOLVE_V4,
        CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_1_1,

        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_MAXREDIRS      => 0,

        CURLOPT_ENCODING       => '',
        CURLOPT_USERAGENT      => 'Nexus-PHP-Game-Sync/1.0',

        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
            'Content-Type: application/json; charset=utf-8',
            'Content-Length: ' . strlen($jsonPayload),
            'Connection: close',
        ],
    ]);

    $responseBody = curl_exec($ch);

    $curlError = curl_error($ch);
    $curlErrno = curl_errno($ch);
    $curlInfo = curl_getinfo($ch);

    $httpCode = isset($curlInfo['http_code'])
        ? (int) $curlInfo['http_code']
        : 0;

    $localIp = isset($curlInfo['local_ip'])
        ? (string) $curlInfo['local_ip']
        : '';

    $remoteIp = isset($curlInfo['primary_ip'])
        ? (string) $curlInfo['primary_ip']
        : '';

    $contentType = isset($curlInfo['content_type'])
        ? (string) $curlInfo['content_type']
        : '';

    curl_close($ch);

    /*
    |--------------------------------------------------------------------------
    | Error koneksi cURL
    |--------------------------------------------------------------------------
    */

    if ($responseBody === false) {
        error_log(json_encode([
            'event'         => 'NEXUS_GAME_CURL_FAILED',
            'provider_code' => $providerCode,
            'curl_errno'    => $curlErrno,
            'curl_error'    => $curlError,
        ], JSON_UNESCAPED_UNICODE));

        nexusGameJsonResponse([
            'success'    => false,
            'message'    => 'Tidak dapat terhubung ke API Nexus.',
            'curl_errno' => $curlErrno,
            'curl_error' => $curlError,
        ], 502);
    }

    /*
    |--------------------------------------------------------------------------
    | Decode JSON
    |--------------------------------------------------------------------------
    */

    $response = json_decode(
        (string) $responseBody,
        true
    );

    if (!is_array($response)) {
        $safeBody = trim(
            preg_replace(
                '/\s+/',
                ' ',
                strip_tags((string) $responseBody)
            )
        );

        error_log(json_encode([
            'event'         => 'NEXUS_GAME_INVALID_JSON',
            'provider_code' => $providerCode,
            'http_code'     => $httpCode,
            'content_type'  => $contentType,
            'local_ip'      => $localIp,
            'remote_ip'     => $remoteIp,
            'body_preview'  => substr(
                $safeBody,
                0,
                1000
            ),
        ], JSON_UNESCAPED_UNICODE));

        nexusGameJsonResponse([
            'success'   => false,
            'message'   => 'Respons Nexus bukan JSON yang valid.',
            'http_code' => $httpCode,
            'response'  => substr(
                $safeBody,
                0,
                1000
            ),
        ], 502);
    }

    /*
    |--------------------------------------------------------------------------
    | HTTP error
    |--------------------------------------------------------------------------
    */

    if ($httpCode < 200 || $httpCode >= 300) {
        $nexusMessage = isset($response['msg'])
            ? trim((string) $response['msg'])
            : 'Tidak ada pesan dari Nexus.';

        error_log(json_encode([
            'event'         => 'NEXUS_GAME_HTTP_ERROR',
            'provider_code' => $providerCode,
            'http_code'     => $httpCode,
            'local_ip'      => $localIp,
            'remote_ip'     => $remoteIp,
            'nexus_message' => substr(
                $nexusMessage,
                0,
                500
            ),
        ], JSON_UNESCAPED_UNICODE));

        nexusGameJsonResponse([
            'success'       => false,
            'provider_code' => $providerCode,
            'http_code'     => $httpCode,
            'message'       => 'API Nexus menolak request game_list.',
            'nexus_message' => $nexusMessage,
        ], $httpCode === 403 ? 403 : 502);
    }

    /*
    |--------------------------------------------------------------------------
    | Validasi status Nexus
    |--------------------------------------------------------------------------
    */

    $nexusStatus = isset($response['status'])
        ? (int) $response['status']
        : 0;

    $nexusMessage = isset($response['msg'])
        ? trim((string) $response['msg'])
        : 'Unknown Nexus error';

    if ($nexusStatus !== 1) {
        error_log(json_encode([
            'event'         => 'NEXUS_GAME_API_REJECTED',
            'provider_code' => $providerCode,
            'nexus_status'  => $nexusStatus,
            'nexus_message' => substr(
                $nexusMessage,
                0,
                500
            ),
        ], JSON_UNESCAPED_UNICODE));

        nexusGameJsonResponse([
            'success'       => false,
            'provider_code' => $providerCode,
            'status'        => $nexusStatus,
            'message'       => 'Nexus gagal memproses game_list.',
            'nexus_message' => $nexusMessage,
        ], 502);
    }

    $games = isset($response['games'])
        ? $response['games']
        : null;

    if (!is_array($games)) {
        nexusGameJsonResponse([
            'success'       => false,
            'provider_code' => $providerCode,
            'message'       => 'Data games tidak ditemukan pada respons Nexus.',
        ], 502);
    }

    /*
    |--------------------------------------------------------------------------
    | Lindungi database jika API mengembalikan array kosong
    |--------------------------------------------------------------------------
    |
    | Data lama tidak langsung dinonaktifkan agar respons API kosong
    | sementara tidak merusak seluruh daftar game.
    */

    if (count($games) === 0) {
        nexusGameJsonResponse([
            'success'       => true,
            'provider_code' => $providerCode,
            'provider_name' => $providerName,
            'message'       => 'Nexus mengembalikan daftar game kosong. Database tidak diubah.',

            'summary' => [
                'total_api' => 0,
                'processed' => 0,
                'inserted'  => 0,
                'updated'   => 0,
                'unchanged' => 0,
                'disabled'  => 0,
                'skipped'   => 0,
            ],

            'warning' => 'Respons kosong tidak digunakan untuk menonaktifkan game lama.',
        ]);
    }

    /*
    |--------------------------------------------------------------------------
    | Query upsert game
    |--------------------------------------------------------------------------
    |
    | Field yang tidak ditimpa saat sinkronisasi:
    |
    | game_type
    | is_active
    | is_popular
    | popular_order
    */

    $sql = "
        INSERT INTO games_nexus (
            provider_id,
            provider_code,
            game_code,
            game_name,
            game_type,
            game_image,
            game_status,
            is_active,
            is_popular,
            popular_order,
            sync_token,
            last_synced_at
        ) VALUES (
            :provider_id,
            :provider_code,
            :game_code,
            :game_name,
            'UNMAPPED',
            :game_image,
            :game_status,
            1,
            0,
            0,
            :sync_token,
            NOW()
        )
        ON DUPLICATE KEY UPDATE
            provider_code  = VALUES(provider_code),
            game_name      = VALUES(game_name),
            game_image     = VALUES(game_image),
            game_status    = VALUES(game_status),
            sync_token     = VALUES(sync_token),
            last_synced_at = NOW()
    ";

    try {
        $gameStatement = $pdo->prepare($sql);
    } catch (Throwable $e) {
        error_log(
            'Prepare games_nexus failed: ' .
            $e->getMessage()
        );

        nexusGameJsonResponse([
            'success' => false,
            'message' => 'Gagal menyiapkan query games_nexus.',
        ], 500);
    }

    $syncToken = bin2hex(random_bytes(16));

    $processed = 0;
    $inserted = 0;
    $updated = 0;
    $unchanged = 0;
    $disabled = 0;
    $skipped = 0;
    $errors = [];

    /*
    |--------------------------------------------------------------------------
    | Simpan games
    |--------------------------------------------------------------------------
    */

    try {
        $pdo->beginTransaction();

        foreach ($games as $index => $game) {
            if (!is_array($game)) {
                $skipped++;

                $errors[] =
                    'Game index ' . $index . ' bukan array.';

                continue;
            }

            $gameCode = isset($game['game_code'])
                ? trim((string) $game['game_code'])
                : '';

            $gameName = isset($game['game_name'])
                ? trim((string) $game['game_name'])
                : '';

            $gameImage = isset($game['banner'])
                ? trim((string) $game['banner'])
                : '';

            $gameStatus = (
                isset($game['status']) &&
                (int) $game['status'] === 1
            ) ? 1 : 0;

            /*
             * Validasi field wajib.
             */

            if (
                $gameCode === '' ||
                $gameName === ''
            ) {
                $skipped++;

                $errors[] =
                    'Game tidak lengkap pada index ' .
                    $index . '.';

                continue;
            }

            /*
             * Batasi panjang sesuai database.
             */

            if (strlen($gameCode) > 150) {
                $skipped++;

                $errors[] =
                    'Game code terlalu panjang pada index ' .
                    $index . '.';

                continue;
            }

            $gameName = nexusGameCutText(
                $gameName,
                255
            );

            /*
             * Hanya menerima URL banner HTTPS.
             */

            if (
                $gameImage === '' ||
                filter_var(
                    $gameImage,
                    FILTER_VALIDATE_URL
                ) === false ||
                strtolower(
                    (string) parse_url(
                        $gameImage,
                        PHP_URL_SCHEME
                    )
                ) !== 'https'
            ) {
                $gameImage = null;
            }

            $gameStatement->execute([
                'provider_id'   => $providerId,
                'provider_code' => $providerCode,
                'game_code'     => $gameCode,
                'game_name'     => $gameName,
                'game_image'    => $gameImage,
                'game_status'   => $gameStatus,
                'sync_token'    => $syncToken,
            ]);

            $affectedRows = $gameStatement->rowCount();

            if ($affectedRows === 1) {
                $inserted++;
            } elseif ($affectedRows === 2) {
                $updated++;
            } else {
                $unchanged++;
            }

            $processed++;
        }

        /*
        |--------------------------------------------------------------------------
        | Jangan lanjut jika semua data game gagal validasi
        |--------------------------------------------------------------------------
        */

        if ($processed === 0) {
            $pdo->rollBack();

            nexusGameJsonResponse([
                'success'       => false,
                'provider_code' => $providerCode,
                'message'       => 'Tidak ada game valid yang dapat disimpan.',
                'errors'        => array_slice(
                    $errors,
                    0,
                    20
                ),
            ], 422);
        }

        /*
        |--------------------------------------------------------------------------
        | Nonaktifkan game lama yang tidak lagi dikirim API
        |--------------------------------------------------------------------------
        |
        | Hanya game_status yang diubah.
        | is_active milik admin tetap tidak disentuh.
        */

        $disableStatement = $pdo->prepare("
            UPDATE games_nexus
            SET
                game_status = 0,
                last_synced_at = NOW()
            WHERE provider_id = :provider_id
              AND (
                    sync_token IS NULL
                    OR sync_token <> :sync_token
              )
        ");

        $disableStatement->execute([
            'provider_id' => $providerId,
            'sync_token'  => $syncToken,
        ]);

        $disabled = $disableStatement->rowCount();

        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) {
            $pdo->rollBack();
        }

        error_log(json_encode([
            'event'         => 'NEXUS_GAME_SYNC_FAILED',
            'provider_id'   => $providerId,
            'provider_code' => $providerCode,
            'message'       => $e->getMessage(),
            'file'          => $e->getFile(),
            'line'          => $e->getLine(),
            'admin_id'      => isset($_SESSION['admin_id'])
                ? $_SESSION['admin_id']
                : null,
        ], JSON_UNESCAPED_UNICODE));

        nexusGameJsonResponse([
            'success'       => false,
            'provider_code' => $providerCode,
            'message'       => 'Sinkronisasi game Nexus gagal.',
            'detail'        => 'Periksa error log server.',
        ], 500);
    }

    /*
    |--------------------------------------------------------------------------
    | Respons sukses
    |--------------------------------------------------------------------------
    */

    nexusGameJsonResponse([
        'success'       => true,
        'provider_code' => $providerCode,
        'provider_name' => $providerName,
        'message'       => 'Game Nexus berhasil disinkronkan.',

        'summary' => [
            'total_api' => count($games),
            'processed' => $processed,
            'inserted'  => $inserted,
            'updated'   => $updated,
            'unchanged' => $unchanged,
            'disabled'  => $disabled,
            'skipped'   => $skipped,
        ],

        'connection' => [
            'http_code' => $httpCode,
            'local_ip'  => $localIp,
            'remote_ip' => $remoteIp,
        ],

        'errors' => array_slice(
            $errors,
            0,
            20
        ),
    ]);
}

/*
|--------------------------------------------------------------------------
| Hanya GET dan POST
|--------------------------------------------------------------------------
*/

if (
    !isset($_SERVER['REQUEST_METHOD']) ||
    $_SERVER['REQUEST_METHOD'] !== 'GET'
) {
    header('Allow: GET, POST');

    nexusGameJsonResponse([
        'success' => false,
        'message' => 'Metode request tidak diperbolehkan.',
    ], 405);
}

/*
|--------------------------------------------------------------------------
| GET: ambil daftar provider aktif
|--------------------------------------------------------------------------
*/

try {
    $providerQuery = $pdo->query("
        SELECT
            id,
            provider_code,
            provider_name
        FROM providers_nexus
        WHERE provider_status = 1
        ORDER BY provider_name ASC
    ");

    $providers = $providerQuery->fetchAll(
        PDO::FETCH_ASSOC
    );
} catch (Throwable $e) {
    error_log(
        'Nexus provider list error: ' .
        $e->getMessage()
    );

    http_response_code(500);

    exit(
        'Gagal mengambil daftar provider Nexus: ' .
        htmlspecialchars(
            $e->getMessage(),
            ENT_QUOTES,
            'UTF-8'
        )
    );
}

$providerCodes = [];

foreach ($providers as $provider) {
    $providerCodes[] = (string) $provider['provider_code'];
}

$csrfToken = (string) $_SESSION['csrf_sync_games_nexus'];

$scriptUrl = isset($_SERVER['SCRIPT_NAME'])
    ? (string) $_SERVER['SCRIPT_NAME']
    : 'sync_games_nexus.php';

?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">

    <meta
        name="viewport"
        content="width=device-width, initial-scale=1"
    >

    <title>Sinkronisasi Game Nexus</title>

    <style>
        * {
            box-sizing: border-box;
        }

        body {
            margin: 0;
            min-height: 100vh;
            padding: 24px;
            background: #071426;
            color: #ffffff;
            font-family: Arial, Helvetica, sans-serif;
        }

        .container {
            width: 100%;
            max-width: 1050px;
            margin: 0 auto;
        }

        .card {
            padding: 25px;
            border: 1px solid rgba(255, 255, 255, .12);
            border-radius: 18px;
            background: #0d2038;
            box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
        }

        h1 {
            margin: 0 0 8px;
            font-size: 28px;
        }

        .subtitle {
            margin: 0 0 24px;
            color: #b9cbe0;
            line-height: 1.6;
        }

        .notice {
            margin-bottom: 20px;
            padding: 14px 16px;
            border-radius: 12px;
            background: #08192c;
            color: #c9d8e8;
            font-size: 14px;
            line-height: 1.6;
        }

        .actions {
            display: grid;
            grid-template-columns: 1fr auto auto;
            gap: 12px;
            margin-bottom: 15px;
        }

        select,
        button {
            min-height: 47px;
            border-radius: 10px;
            font-size: 15px;
        }

        select {
            width: 100%;
            padding: 0 12px;
            border: 1px solid rgba(255, 255, 255, .15);
            background: #08192c;
            color: #ffffff;
        }

        button {
            border: 0;
            padding: 0 19px;
            background: #f2b632;
            color: #111827;
            font-weight: 700;
            cursor: pointer;
        }

        button.secondary {
            background: #dbeafe;
        }

        button.stop {
            width: 100%;
            margin-bottom: 15px;
            background: #ef4444;
            color: #ffffff;
        }

        button:disabled {
            cursor: not-allowed;
            opacity: .55;
        }

        .progress-wrapper {
            height: 18px;
            margin: 20px 0 8px;
            overflow: hidden;
            border-radius: 999px;
            background: #06101e;
        }

        .progress-bar {
            width: 0;
            height: 100%;
            background: linear-gradient(
                90deg,
                #f2b632,
                #ffd972
            );
            transition: width .25s ease;
        }

        .progress-text {
            margin-bottom: 20px;
            color: #c8d7e8;
            font-size: 14px;
        }

        .stats {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            gap: 12px;
            margin-bottom: 20px;
        }

        .stat {
            padding: 14px;
            border-radius: 12px;
            background: #08192c;
            text-align: center;
        }

        .stat strong {
            display: block;
            margin-bottom: 4px;
            font-size: 22px;
        }

        .stat span {
            color: #9fb4ca;
            font-size: 12px;
        }

        .log {
            height: 420px;
            overflow: auto;
            padding: 16px;
            border-radius: 12px;
            background: #030b15;
            color: #d6e4f2;
            font-family: Consolas, monospace;
            font-size: 13px;
            line-height: 1.6;
        }

        .log-line {
            margin-bottom: 5px;
        }

        .success {
            color: #7ef2a8;
        }

        .error {
            color: #ff8c8c;
        }

        .warning {
            color: #ffd972;
        }

        @media (max-width: 760px) {
            body {
                padding: 12px;
            }

            .actions {
                grid-template-columns: 1fr;
            }

            .stats {
                grid-template-columns: repeat(2, 1fr);
            }

            .log {
                height: 350px;
            }
        }
    </style>
</head>

<body>
<div class="container">
    <div class="card">
        <h1>Sinkronisasi Game Nexus</h1>

        <p class="subtitle">
            Mengambil game Nexus satu provider per request agar
            proses tidak mudah timeout.
        </p>

        <div class="notice">
            API Nexus tidak mengirim game_type. Game baru akan
            disimpan sebagai <strong>UNMAPPED</strong>.
            Nilai game_type, is_active, is_popular dan
            popular_order tidak akan direset saat sinkronisasi.
        </div>

        <div class="actions">
            <select id="providerSelect">
                <option value="">
                    Pilih provider Nexus
                </option>

                <?php foreach ($providers as $provider): ?>
                    <option
                        value="<?= htmlspecialchars(
                            (string) $provider['provider_code'],
                            ENT_QUOTES,
                            'UTF-8'
                        ) ?>"
                    >
                        <?= htmlspecialchars(
                            (string) $provider['provider_name'],
                            ENT_QUOTES,
                            'UTF-8'
                        ) ?>
                        —
                        <?= htmlspecialchars(
                            (string) $provider['provider_code'],
                            ENT_QUOTES,
                            'UTF-8'
                        ) ?>
                    </option>
                <?php endforeach; ?>
            </select>

            <button
                type="button"
                id="syncSelectedButton"
                class="secondary"
            >
                Sync Pilihan
            </button>

            <button
                type="button"
                id="syncAllButton"
            >
                Sync Semua
            </button>
        </div>

        <button
            type="button"
            id="stopButton"
            class="stop"
            disabled
        >
            Hentikan Proses
        </button>

        <div class="progress-wrapper">
            <div
                class="progress-bar"
                id="progressBar"
            ></div>
        </div>

        <div
            class="progress-text"
            id="progressText"
        >
            Belum ada proses sinkronisasi.
        </div>

        <div class="stats">
            <div class="stat">
                <strong id="providerCount">0</strong>
                <span>Provider selesai</span>
            </div>

            <div class="stat">
                <strong id="gameCount">0</strong>
                <span>Game diproses</span>
            </div>

            <div class="stat">
                <strong id="insertedCount">0</strong>
                <span>Game baru</span>
            </div>

            <div class="stat">
                <strong id="updatedCount">0</strong>
                <span>Game update</span>
            </div>

            <div class="stat">
                <strong id="failedCount">0</strong>
                <span>Provider gagal</span>
            </div>
        </div>

        <div class="log" id="logBox">
            <div class="log-line">
                Siap melakukan sinkronisasi Nexus.
            </div>
        </div>
    </div>
</div>

<script>
'use strict';

const endpoint = <?= json_encode(
    $scriptUrl,
    JSON_UNESCAPED_SLASHES
) ?>;

const csrfToken = <?= json_encode(
    $csrfToken,
    JSON_UNESCAPED_SLASHES
) ?>;

const providerCodes = <?= json_encode(
    $providerCodes,
    JSON_UNESCAPED_SLASHES |
    JSON_UNESCAPED_UNICODE
) ?>;

const providerSelect = document.getElementById(
    'providerSelect'
);

const syncSelectedButton = document.getElementById(
    'syncSelectedButton'
);

const syncAllButton = document.getElementById(
    'syncAllButton'
);

const stopButton = document.getElementById(
    'stopButton'
);

const progressBar = document.getElementById(
    'progressBar'
);

const progressText = document.getElementById(
    'progressText'
);

const logBox = document.getElementById(
    'logBox'
);

const providerCount = document.getElementById(
    'providerCount'
);

const gameCount = document.getElementById(
    'gameCount'
);

const insertedCount = document.getElementById(
    'insertedCount'
);

const updatedCount = document.getElementById(
    'updatedCount'
);

const failedCount = document.getElementById(
    'failedCount'
);

let running = false;
let shouldStop = false;

let totals = {
    providers: 0,
    games: 0,
    inserted: 0,
    updated: 0,
    failed: 0
};

function resetTotals() {
    totals.providers = 0;
    totals.games = 0;
    totals.inserted = 0;
    totals.updated = 0;
    totals.failed = 0;

    updateStats();
}

function updateStats() {
    providerCount.textContent =
        String(totals.providers);

    gameCount.textContent =
        String(totals.games);

    insertedCount.textContent =
        String(totals.inserted);

    updatedCount.textContent =
        String(totals.updated);

    failedCount.textContent =
        String(totals.failed);
}

function addLog(message, type) {
    const date = new Date();

    const time = date.toLocaleTimeString(
        'id-ID'
    );

    const line = document.createElement('div');

    line.className =
        'log-line' + (type ? ' ' + type : '');

    line.textContent =
        '[' + time + '] ' + message;

    logBox.appendChild(line);
    logBox.scrollTop = logBox.scrollHeight;
}

function setRunning(status) {
    running = status;

    syncSelectedButton.disabled = status;
    syncAllButton.disabled = status;
    providerSelect.disabled = status;
    stopButton.disabled = !status;
}

async function syncProvider(providerCode) {
    const formData = new URLSearchParams();

    formData.set(
        'csrf_token',
        csrfToken
    );

    formData.set(
        'provider_code',
        providerCode
    );

    const response = await fetch(endpoint, {
        method: 'POST',

        headers: {
            'Accept': 'application/json',

            'Content-Type':
                'application/x-www-form-urlencoded;charset=UTF-8'
        },

        credentials: 'same-origin',

        body: formData.toString()
    });

    let result;

    try {
        result = await response.json();
    } catch (error) {
        throw new Error(
            'Respons bukan JSON. HTTP ' +
            response.status
        );
    }

    if (
        !response.ok ||
        result.success !== true
    ) {
        const message =
            result.nexus_message ||
            result.message ||
            ('HTTP ' + response.status);

        throw new Error(message);
    }

    return result;
}

async function runSync(codes) {
    if (
        running ||
        !Array.isArray(codes) ||
        codes.length === 0
    ) {
        return;
    }

    shouldStop = false;

    resetTotals();

    logBox.innerHTML = '';

    setRunning(true);

    addLog(
        'Memulai sinkronisasi ' +
        codes.length +
        ' provider Nexus.',
        'warning'
    );

    for (
        let index = 0;
        index < codes.length;
        index++
    ) {
        if (shouldStop) {
            addLog(
                'Proses dihentikan oleh admin.',
                'warning'
            );

            break;
        }

        const providerCode = codes[index];

        progressText.textContent =
            'Memproses ' +
            providerCode +
            ' (' +
            (index + 1) +
            ' dari ' +
            codes.length +
            ')';

        progressBar.style.width =
            Math.round(
                (index / codes.length) * 100
            ) + '%';

        addLog(
            'Mengambil game ' +
            providerCode +
            '...'
        );

        try {
            const result = await syncProvider(
                providerCode
            );

            const summary = result.summary || {};

            totals.providers++;

            totals.games += Number(
                summary.processed || 0
            );

            totals.inserted += Number(
                summary.inserted || 0
            );

            totals.updated += Number(
                summary.updated || 0
            );

            updateStats();

            let message =
                providerCode +
                ' berhasil — ' +
                Number(summary.processed || 0) +
                ' game, ' +
                Number(summary.inserted || 0) +
                ' baru, ' +
                Number(summary.updated || 0) +
                ' update, ' +
                Number(summary.disabled || 0) +
                ' dinonaktifkan.';

            if (result.warning) {
                message += ' ' + result.warning;
            }

            addLog(
                message,
                result.warning
                    ? 'warning'
                    : 'success'
            );
        } catch (error) {
            totals.failed++;

            updateStats();

            addLog(
                providerCode +
                ' gagal — ' +
                error.message,
                'error'
            );
        }

        progressBar.style.width =
            Math.round(
                ((index + 1) / codes.length) *
                100
            ) + '%';

        /*
         * Jeda antar-provider agar request
         * tidak terlalu agresif.
         */
        await new Promise(function (resolve) {
            setTimeout(resolve, 250);
        });
    }

    setRunning(false);

    if (shouldStop) {
        progressText.textContent =
            'Sinkronisasi dihentikan.';
    } else {
        progressBar.style.width = '100%';

        progressText.textContent =
            'Sinkronisasi selesai.';

        addLog(
            'Seluruh sinkronisasi Nexus selesai.',
            'success'
        );
    }
}

syncSelectedButton.addEventListener(
    'click',
    function () {
        const selected =
            providerSelect.value.trim();

        if (!selected) {
            addLog(
                'Pilih provider terlebih dahulu.',
                'warning'
            );

            return;
        }

        runSync([selected]);
    }
);

syncAllButton.addEventListener(
    'click',
    function () {
        runSync(providerCodes);
    }
);

stopButton.addEventListener(
    'click',
    function () {
        shouldStop = true;
        stopButton.disabled = true;

        addLog(
            'Permintaan menghentikan proses diterima.',
            'warning'
        );
    }
);
</script>
</body>
</html>