
<?php
ob_start();
session_start();

error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);

require_once __DIR__ . '/config.php';
$conn->set_charset("utf8mb4");

$nama = '2 SD|PAI_6|Membaca dan memahami teks sederhana';
$judulKuis = $nama;

/* =========================================================
   PENGATURAN KUIS TERPUSAT
   UBAH ANGKA DI BAGIAN INI SAJA
========================================================= */
$jedaSoalBerikutnya = 1200; // Jeda menuju soal berikutnya (milidetik)
$maksimalSoal = 50;         // Maksimal jumlah soal
$intervalHeartbeat = 10000; // Interval heartbeat (milidetik)

/* =========================================================
   FUNGSI RESPONS JSON
========================================================= */
function kirimJson($data, $kode = 200)
{
    while (ob_get_level() > 0) {
        ob_end_clean();
    }

    http_response_code($kode);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');

    echo json_encode(
        $data,
        JSON_UNESCAPED_UNICODE |
        JSON_UNESCAPED_SLASHES |
        JSON_INVALID_UTF8_SUBSTITUTE
    );
    exit;
}

/* =========================================================
   ENDPOINT HEARTBEAT
========================================================= */
if (
    $_SERVER['REQUEST_METHOD'] === 'POST'
    && isset($_POST['aksi_pantau'])
) {
    ini_set('display_errors', '0');
    ini_set('log_errors', '1');

    $aksi = trim((string) $_POST['aksi_pantau']);
    $token = trim((string) ($_POST['token'] ?? ''));

    if (!preg_match('/^[a-f0-9]{64}$/', $token)) {
        kirimJson([
            'ok' => false,
            'pesan' => 'Token tidak valid.'
        ], 400);
    }

    /* MULAI */
    if ($aksi === 'mulai') {
        $nama = trim((string) ($_POST['nama_siswa'] ?? ''));
        $kelas = trim((string) ($_POST['kelas'] ?? ''));
        $total = max(1, min(50, (int) ($_POST['total_soal'] ?? 50)));

        if ($nama === '' || $kelas === '') {
            kirimJson([
                'ok' => false,
                'pesan' => 'Nama dan kelas siswa wajib diisi.'
            ], 400);
        }

        $status = 'aktif';

        $sql = "INSERT INTO madin_heartbeat (
                    judul, token, nama_siswa, kelas, status,
                    total_soal, soal_dikerjakan, jumlah_benar,
                    nilai, mulai_pada, aktivitas_terakhir, selesai_pada
                )
                VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, NOW(), NOW(), NULL)
                ON DUPLICATE KEY UPDATE
                    judul = VALUES(judul),
                    nama_siswa = VALUES(nama_siswa),
                    kelas = VALUES(kelas),
                    status = 'aktif',
                    total_soal = VALUES(total_soal),
                    soal_dikerjakan = 0,
                    jumlah_benar = 0,
                    nilai = 0,
                    mulai_pada = NOW(),
                    aktivitas_terakhir = NOW(),
                    selesai_pada = NULL";

        $stmt = $conn->prepare($sql);

        if (!$stmt) {
            error_log('Heartbeat mulai prepare: ' . $conn->error);
            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal menyiapkan data heartbeat.'
            ], 500);
        }

        $stmt->bind_param(
            'sssssi',
            $judulKuis,
            $token,
            $nama,
            $kelas,
            $status,
            $total
        );

        if (!$stmt->execute()) {
            error_log('Heartbeat mulai execute: ' . $stmt->error);
            $stmt->close();

            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal menyimpan data awal kuis.'
            ], 500);
        }

        $stmt->close();

        kirimJson([
            'ok' => true,
            'pesan' => 'Heartbeat dimulai.'
        ]);
    }

    /* PING */
    if ($aksi === 'ping') {
        $stmt = $conn->prepare(
            "UPDATE madin_heartbeat
             SET aktivitas_terakhir = NOW()
             WHERE token = ?
               AND judul = ?
               AND status = 'aktif'"
        );

        if (!$stmt) {
            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal menyiapkan pembaruan aktivitas.'
            ], 500);
        }

        $stmt->bind_param('ss', $token, $judulKuis);

        if (!$stmt->execute()) {
            $stmt->close();
            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal memperbarui aktivitas.'
            ], 500);
        }

        $jumlahBaris = $stmt->affected_rows;
        $stmt->close();

        if ($jumlahBaris === 0) {
            $cek = $conn->prepare(
                "SELECT id
                 FROM madin_heartbeat
                 WHERE token = ? AND judul = ? AND status = 'aktif'
                 LIMIT 1"
            );

            if (!$cek) {
                kirimJson([
                    'ok' => false,
                    'pesan' => 'Gagal memeriksa sesi kuis.'
                ], 500);
            }

            $cek->bind_param('ss', $token, $judulKuis);
            $cek->execute();
            $hasilCek = $cek->get_result();
            $sesiAda = $hasilCek && $hasilCek->num_rows > 0;
            $cek->close();

            if (!$sesiAda) {
                kirimJson([
                    'ok' => false,
                    'pesan' => 'Sesi kuis tidak ditemukan atau sudah selesai.'
                ], 409);
            }
        }

        kirimJson([
            'ok' => true,
            'pesan' => 'Aktivitas diperbarui.'
        ]);
    }

    /* UPDATE PROGRES / SELESAI */
    if ($aksi === 'update' || $aksi === 'selesai') {
        $dikerjakan = max(
            0,
            min(50, (int) ($_POST['soal_dikerjakan'] ?? 0))
        );

        $benar = max(
            0,
            min($dikerjakan, (int) ($_POST['jumlah_benar'] ?? 0))
        );

        $nilai = max(
            0,
            min(100, (int) ($_POST['nilai'] ?? 0))
        );

        $status = $aksi === 'selesai' ? 'selesai' : 'aktif';

        $sql = "UPDATE madin_heartbeat
                SET
                    soal_dikerjakan = LEAST(?, total_soal),
                    jumlah_benar = LEAST(?, total_soal),
                    nilai = LEAST(?, 100),
                    status = ?,
                    aktivitas_terakhir = NOW(),
                    selesai_pada = IF(? = 'selesai', NOW(), selesai_pada)
                WHERE token = ?
                  AND judul = ?
                  AND status = 'aktif'";

        $stmt = $conn->prepare($sql);

        if (!$stmt) {
            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal menyiapkan pembaruan progres.'
            ], 500);
        }

        $stmt->bind_param(
            'iiissss',
            $dikerjakan,
            $benar,
            $nilai,
            $status,
            $status,
            $token,
            $judulKuis
        );

        if (!$stmt->execute()) {
            $stmt->close();
            kirimJson([
                'ok' => false,
                'pesan' => 'Gagal menyimpan progres kuis.'
            ], 500);
        }

        $jumlahBaris = $stmt->affected_rows;
        $stmt->close();

        if ($jumlahBaris === 0) {
            $cek = $conn->prepare(
                "SELECT id
                 FROM madin_heartbeat
                 WHERE token = ? AND judul = ? AND status = 'aktif'
                 LIMIT 1"
            );

            if (!$cek) {
                kirimJson([
                    'ok' => false,
                    'pesan' => 'Gagal memeriksa sesi kuis.'
                ], 500);
            }

            $cek->bind_param('ss', $token, $judulKuis);
            $cek->execute();
            $hasilCek = $cek->get_result();
            $sesiAktif = $hasilCek && $hasilCek->num_rows > 0;
            $cek->close();

            if (!$sesiAktif) {
                kirimJson([
                    'ok' => false,
                    'pesan' => 'Sesi kuis tidak aktif atau tidak ditemukan.'
                ], 409);
            }
        }

        kirimJson([
            'ok' => true,
            'pesan' => $aksi === 'selesai'
                ? 'Kuis selesai tercatat.'
                : 'Progres kuis diperbarui.'
        ]);
    }

    kirimJson([
        'ok' => false,
        'pesan' => 'Aksi heartbeat tidak dikenal.'
    ], 400);
}

/* =========================================================
   CEK NILAI LAMA BERDASARKAN NAMA + KELAS + JUDUL
   - Nilai sama: tidak menambah data duplikat.
   - Nilai berbeda: tambah data baru.
   - NIS boleh kosong.
========================================================= */

$stmt = $conn->prepare(
    "SELECT id, nilai
     FROM fun
     WHERE LOWER(TRIM(nama_siswa)) = LOWER(TRIM(?))
       AND LOWER(TRIM(kelas)) = LOWER(TRIM(?))
       AND LOWER(TRIM(judul)) = LOWER(TRIM(?))
     ORDER BY id DESC
     LIMIT 1"
);

if (!$stmt) {
    kirimJson([
        'ok' => false,
        'pesan' => 'Gagal menyiapkan pengecekan nilai: ' . $conn->error
    ], 500);
}

$stmt->bind_param('sss', $namaSiswa, $kelasSiswa, $judul);

if (!$stmt->execute()) {
    $error = $stmt->error;
    $stmt->close();

    kirimJson([
        'ok' => false,
        'pesan' => 'Gagal mengecek nilai: ' . $error
    ], 500);
}

$hasil = $stmt->get_result();
$dataLama = $hasil->fetch_assoc();
$stmt->close();

/* Jika nilai sudah ada dan sama, tidak perlu menambah data */
if ($dataLama && (int)$dataLama['nilai'] === $nilai) {
    kirimJson([
        'ok' => true,
        'aksi' => 'tetap',
        'sudah_ada' => true,
        'pesan' => 'Nilai sama dengan data sebelumnya. Tidak ada data baru yang ditambahkan.',
        'nama_siswa' => $namaSiswa,
        'kelas' => $kelasSiswa,
        'nilai' => $nilai
    ]);
}

/* Jika belum ada atau nilainya berbeda, tambahkan data baru */
$nis = '';

$stmt = $conn->prepare(
    "INSERT INTO fun
        (nis, nama_siswa, kelas, judul, nilai, submited_at)
     VALUES (?, ?, ?, ?, ?, NOW())"
);

if (!$stmt) {
    kirimJson([
        'ok' => false,
        'pesan' => 'Gagal menyiapkan penyimpanan nilai: ' . $conn->error
    ], 500);
}

$stmt->bind_param(
    'ssssi',
    $nis,
    $namaSiswa,
    $kelasSiswa,
    $judul,
    $nilai
);

if (!$stmt->execute()) {
    $error = $stmt->error;
    $stmt->close();

    kirimJson([
        'ok' => false,
        'pesan' => 'Gagal menyimpan nilai: ' . $error
    ], 500);
}

$idBaru = $stmt->insert_id;
$stmt->close();

kirimJson([
    'ok' => true,
    'aksi' => 'insert',
    'sudah_ada' => false,
    'id' => $idBaru,
    'pesan' => 'Nilai berhasil ditambahkan sebagai data baru.',
    'nama_siswa' => $namaSiswa,
    'kelas' => $kelasSiswa,
    'nilai' => $nilai
]);
/* =========================================================
   AMBIL DAFTAR SANTRI
========================================================= */
$daftarSantri = [];

$querySantri = $conn->query(
    "SELECT id, nama_siswa, kelas
     FROM santri
     WHERE TRIM(COALESCE(nama_siswa, '')) <> ''
       AND TRIM(COALESCE(kelas, '')) <> ''
     ORDER BY kelas ASC, nama_siswa ASC"
);

if ($querySantri) {
    while ($baris = $querySantri->fetch_assoc()) {
        $daftarSantri[] = $baris;
    }
}

/* =========================================================
   AMBIL SOAL DARI TABEL soal
   FILTER NAMA = LAILA
========================================================= */
$bankSoalDb = [];

$stmtSoal = $conn->prepare(
    "SELECT
        id,
        soal,
        pilihan_a,
        pilihan_b,
        pilihan_c,
        pilihan_d,
        pilihan_e,
        pilihan_f,
        jawaban,
        pembahasan,
        waktu_per_soal
     FROM soal
     WHERE nama = ?
       AND TRIM(COALESCE(soal, '')) <> ''
     ORDER BY RAND()
     LIMIT 50"
);

if ($stmtSoal) {
    $stmtSoal->bind_param('s', $nama);
    $stmtSoal->execute();
    $hasilSoal = $stmtSoal->get_result();

    while ($row = $hasilSoal->fetch_assoc()) {
        $opsi = [];

        foreach (['a', 'b', 'c', 'd', 'e', 'f'] as $huruf) {
            $kolom = 'pilihan_' . $huruf;
            $teksOpsi = trim((string) ($row[$kolom] ?? ''));

            if ($teksOpsi !== '') {
                $opsi[strtoupper($huruf)] = $teksOpsi;
            }
        }

        $jawabanAsli = trim((string) ($row['jawaban'] ?? ''));
        $jawabanBenar = '';

        /*
         * Mendukung jawaban berupa:
         * A, B, C, D, E, F
         * pilihan_a, pilihan_b, dan seterusnya
         * atau teks jawaban langsung.
         */
        if (preg_match('/^[A-F]$/i', $jawabanAsli)) {
            $jawabanBenar = $opsi[strtoupper($jawabanAsli)] ?? '';
        } elseif (preg_match('/^pilihan_([a-f])$/i', $jawabanAsli, $cocok)) {
            $jawabanBenar = $opsi[strtoupper($cocok[1])] ?? '';
        } else {
            foreach ($opsi as $teksOpsi) {
                if (strcasecmp($teksOpsi, $jawabanAsli) === 0) {
                    $jawabanBenar = $teksOpsi;
                    break;
                }
            }
        }

        if ($jawabanBenar === '' || count($opsi) < 2) {
            continue;
        }

        $bankSoalDb[] = [
    'id' => (int) $row['id'],
    'teks' => (string) $row['soal'],
    'pilihan' => array_values($opsi),
    'jawaban' => $jawabanBenar,
    'pembahasan' => (string) ($row['pembahasan'] ?? ''),
    'waktu_per_soal' => max(
        1,
        (int) ($row['waktu_per_soal'] ?? 7)
    )
];
    }

    $stmtSoal->close();
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kuis Matematika Dasar SMP</title>

<style>
* { box-sizing: border-box; }

body {
    margin: 0;
    padding: 20px;
    font-family: Arial, sans-serif;
    background: #f1f5f9;
    color: #172033;
}

.wadah {
    max-width: 720px;
    margin: 0 auto;
    background: #fff;
    border-radius: 18px;
    padding: 24px;
    box-shadow: 0 8px 28px rgba(15, 23, 42, .10);
}

h1 {
    margin: 0 0 8px;
    text-align: center;
    font-size: 27px;
}

.subjudul {
    text-align: center;
    color: #64748b;
    margin: 0 0 22px;
}

.ringkasan {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.chip {
    background: #eef2ff;
    color: #3730a3;
    padding: 9px 12px;
    border-radius: 10px;
    font-weight: bold;
}

.bar-latar {
    width: 100%;
    height: 14px;
    background: #e2e8f0;
    border-radius: 20px;
    overflow: hidden;
    margin: 10px 0 22px;
}

#barWaktu {
    width: 100%;
    height: 100%;
    background: #16a34a;
    transition: width .1s linear, background .2s;
}

#layarAwal, #layarAkhir {
    text-align: center;
    padding: 20px 5px;
}

#areaKuis { display: none; }

#pertanyaan {
    text-align: center;
    font-size: clamp(28px, 5vw, 42px);
    font-weight: bold;
    margin: 28px 0;
    overflow-wrap: anywhere;
}

#umpanBalik {
    min-height: 30px;
    text-align: center;
    font-size: 18px;
    font-weight: bold;
    margin: 14px 0;
}

.pilihan {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

button {
    font: inherit;
    cursor: pointer;
    border: 0;
    border-radius: 12px;
    padding: 14px 16px;
    transition: transform .08s, background .15s;
}

button:active { transform: scale(.98); }

.btn-pilihan {
    background: #f8fafc;
    border: 2px solid #cbd5e1;
    font-size: 20px;
    font-weight: bold;
    color: #172033;
    min-height: 62px;
    overflow-wrap: anywhere;
}

.btn-pilihan:hover:not(:disabled) {
    background: #e0f2fe;
    border-color: #0284c7;
}

.btn-pilihan:disabled {
    cursor: default;
    opacity: .85;
}

.btn-utama {
    background: #2563eb;
    color: white;
    font-weight: bold;
    font-size: 17px;
    min-width: 180px;
}

.btn-utama:hover { background: #1d4ed8; }

.btn-utama:disabled {
    opacity: .6;
    cursor: wait;
}

.btn-pilihan.benar {
    background: #dcfce7;
    border-color: #16a34a;
    color: #166534;
}

.btn-pilihan.salah {
    background: #fee2e2;
    border-color: #dc2626;
    color: #991b1b;
}

.detail-akhir {
    display: flex;
    justify-content: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 22px 0;
}

.detail-akhir div {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 12px;
    padding: 16px;
    min-width: 120px;
}

.angka {
    display: block;
    font-size: 28px;
    font-weight: bold;
    margin-bottom: 4px;
}

.catatan {
    color: #64748b;
    font-size: 14px;
    line-height: 1.5;
}

.form-siswa {
    max-width: 460px;
    margin: 20px auto;
    text-align: left;
}

.form-siswa label {
    display: block;
    font-weight: bold;
    margin-bottom: 8px;
}

.form-siswa input,
.form-siswa select {
    display: block;
    width: 100%;
    padding: 13px;
    border: 1px solid #cbd5e1;
    border-radius: 10px;
    font: inherit;
    background: #fff;
    color: #172033;
    margin-bottom: 12px;
}

.form-siswa input:focus,
.form-siswa select:focus {
    outline: 2px solid #93c5fd;
    border-color: #2563eb;
}

.nama-wrapper {
    position: relative;
    width: 100%;
    margin-bottom: 12px;
}

.nama-wrapper > input {
    padding-right: 50px;
    margin-bottom: 0;
}

.btn-dropdown {
    position: absolute;
    top: 3px;
    right: 3px;
    width: 42px;
    height: calc(100% - 6px);
    padding: 0;
    border-radius: 8px;
    background: #f1f5f9;
    color: #334155;
    font-size: 23px;
}

.btn-dropdown:hover {
    background: #dbeafe;
    color: #1d4ed8;
}

.daftar-nama-kustom {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 260px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #cbd5e1;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .15);
    z-index: 1000;
}

.daftar-nama-kustom[hidden] { display: none; }

.opsi-nama {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 12px;
    text-align: left;
    background: #fff;
    color: #172033;
    border-radius: 0;
    border-bottom: 1px solid #e2e8f0;
}

.opsi-nama:hover { background: #eff6ff; }

.opsi-nama small {
    color: #64748b;
    white-space: nowrap;
}

.opsi-nama:last-child { border-bottom: 0; }

#btnMulai {
    display: block;
    margin: 20px auto 0;
}

#statusSimpan {
    min-height: 22px;
    margin: 12px 0;
    font-size: 14px;
    line-height: 1.5;
}

@media (max-width: 480px) {
    body { padding: 10px; }
    .wadah { padding: 17px; }
    h1 { font-size: 23px; }
    .pilihan { grid-template-columns: 1fr; }
    .btn-pilihan { min-height: 52px; }
}


/* =========================================================
   ANIMASI DAN EFEK DARI KUIS SUMBER
========================================================= */

/* Soal muncul dengan efek naik dan memudar */
@keyframes efekMasukSoal {
    from {
        opacity: 0;
        transform: translateY(18px) scale(.97);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

#pertanyaan.animasi-masuk {
    animation: efekMasukSoal .35s ease-out both;
}

.btn-pilihan.animasi-masuk {
    animation: efekMasukSoal .35s ease-out both;
}

/* Pilihan salah bergetar */
.btn-pilihan.salah {
    animation: getar .25s linear 3;
}

@keyframes getar {
    0% { transform: translateX(0); }
    25% { transform: translateX(-8px); }
    50% { transform: translateX(8px); }
    75% { transform: translateX(-8px); }
    100% { transform: translateX(0); }
}

/* Jawaban benar memantul sebentar */
.btn-pilihan.benar {
    animation: efekBenar .4s ease-out;
}

@keyframes efekBenar {
    0% { transform: scale(1); }
    45% { transform: scale(1.06); }
    100% { transform: scale(1); }
}

/* Ledakan di tengah layar */
#ledakan {
    position: fixed;
    left: 50%;
    top: 50%;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    transform: translate(-50%, -50%) scale(0);
    opacity: 0;
    pointer-events: none;
    z-index: 99999;
    background: radial-gradient(
        circle,
        white 0%,
        yellow 12%,
        orange 30%,
        red 50%,
        transparent 72%
    );
}

#ledakan.active {
    animation: boom .7s ease-out;
}

@keyframes boom {
    0% {
        opacity: 1;
        transform: translate(-50%, -50%) scale(.1);
    }
    30% {
        opacity: 1;
        transform: translate(-50%, -50%) scale(5);
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(16);
    }
}

/* Efek teks dan pilihan pecah menyebar */
.hancur {
    animation: hancurkan .7s ease-out forwards !important;
}

@keyframes hancurkan {
    0% {
        opacity: 1;
        transform: translate(0, 0) rotate(0) scale(1);
    }
    25% {
        opacity: 1;
        transform: scale(1.05);
    }
    100% {
        opacity: 0;
        transform: translate(var(--x), var(--y)) rotate(var(--r)) scale(.1);
    }
}

/* Partikel kembang api */
.kembang-api {
    position: fixed;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    pointer-events: none;
    z-index: 99999;
}

@media (prefers-reduced-motion: reduce) {
    #pertanyaan.animasi-masuk,
    .btn-pilihan.animasi-masuk,
    .btn-pilihan.salah,
    .btn-pilihan.benar,
    #ledakan.active,
    .hancur {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
    }
}

</style>
</head>

<body>
<main class="wadah">
<div id="ledakan" aria-hidden="true"></div>

<h1>Kuis <?= htmlspecialchars($nama, ENT_QUOTES, 'UTF-8') ?></h1>
<p class="subjudul">Latihan soal pilihan ganda</p>

<section id="layarAwal">
    <div class="form-siswa">
        <label for="namaManual">Nama siswa</label>

        <div class="nama-wrapper">
            <input
                type="text"
                id="namaManual"
                placeholder="Pilih atau ketik nama siswa"
                maxlength="150"
                autocomplete="off"
            >

            <button
                type="button"
                id="btnDaftarNama"
                class="btn-dropdown"
                aria-label="Tampilkan daftar nama siswa"
                aria-expanded="false"
            >⌄</button>

            <div id="daftarNamaKustom" class="daftar-nama-kustom" hidden>
                <?php foreach ($daftarSantri as $s): ?>
                    <button
                        type="button"
                        class="opsi-nama"
                        data-id="<?= (int) $s['id'] ?>"
                        data-nama="<?= htmlspecialchars($s['nama_siswa'], ENT_QUOTES, 'UTF-8') ?>"
                        data-kelas="<?= htmlspecialchars($s['kelas'], ENT_QUOTES, 'UTF-8') ?>"
                    >
                        <span><?= htmlspecialchars($s['nama_siswa'], ENT_QUOTES, 'UTF-8') ?></span>
                        <small><?= htmlspecialchars($s['kelas'], ENT_QUOTES, 'UTF-8') ?></small>
                    </button>
                <?php endforeach; ?>
            </div>
        </div>

        <label for="kelasManual">Kelas</label>
        <input
            type="text"
            id="kelasManual"
            placeholder="Pilih otomatis atau ketik kelas"
            maxlength="100"
            autocomplete="off"
        >

        <button type="button" class="btn-utama" id="btnMulai">
            Mulai Kuis
        </button>

        <p
            id="pesanAwal"
            role="status"
            aria-live="polite"
            style="text-align:center; min-height:22px; line-height:1.5;"
        ></p>

        <p class="catatan" style="text-align:center">
            Sumber soal: bank soal Laila.
        </p>
    </div>
</section>

<section id="areaKuis">
    <div class="ringkasan">
        <div class="chip" id="nomorSoal">Soal 1</div>
        <div class="chip" id="nilaiSaatIni">Benar: 0</div>
        <div class="chip" id="waktuTeks">Waktu: -- detik</div>
    </div>

    <div class="bar-latar" aria-label="Sisa waktu">
        <div id="barWaktu"></div>
    </div>

    <div id="pertanyaan"></div>
    <div id="pilihan" class="pilihan"></div>
    <div id="umpanBalik" aria-live="polite"></div>

    <div style="text-align:center">
        <button type="button" class="btn-utama" id="btnLanjut" hidden>
            Soal Berikutnya →
        </button>
    </div>
</section>

<section id="layarAkhir" hidden>
    <h2>Kuis Selesai!</h2>
    <p id="pesanAkhir">Hasil latihan Anda</p>

    <div class="detail-akhir">
        <div><span class="angka" id="hasilNilai">0</span>Nilai</div>
        <div><span class="angka" id="hasilBenar">0</span>Benar</div>
        <div><span class="angka" id="hasilSalah">0</span>Salah</div>
        <div><span class="angka" id="hasilLewat">0</span>Tidak dijawab</div>
    </div>

    <p id="statusSimpan" aria-live="polite"></p>

    <button type="button" class="btn-utama" id="btnUlang">
        Main Lagi
    </button>
</section>

</main>


<script>
/* =====================================================
   PENGATURAN KUIS DARI PHP
===================================================== */
const PENGATURAN_KUIS = <?= json_encode([
    'jedaSoalBerikutnya' => $jedaSoalBerikutnya,
    'maksimalSoal' => $maksimalSoal,
    'intervalHeartbeat' => $intervalHeartbeat
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;

/* =====================================================
   BANK SOAL DARI DATABASE PHP
===================================================== */
const BANK_SOAL_DB = <?= json_encode(
    $bankSoalDb,
    JSON_UNESCAPED_UNICODE |
    JSON_UNESCAPED_SLASHES |
    JSON_HEX_TAG |
    JSON_HEX_APOS |
    JSON_HEX_QUOT |
    JSON_HEX_AMP
) ?>;

(() => {
    "use strict";

    /* =====================================================
       SEMUA PROSES KUIS MERUJUK PENGATURAN PHP
    ===================================================== */
    const JEDA_SOAL_BERIKUTNYA =
        PENGATURAN_KUIS.jedaSoalBerikutnya;
    const JUMLAH_SOAL = Math.min(
        PENGATURAN_KUIS.maksimalSoal,
        BANK_SOAL_DB.length
    );
    const INTERVAL_HEARTBEAT =
        PENGATURAN_KUIS.intervalHeartbeat;

    const layarAwal = document.getElementById("layarAwal");
    const areaKuis = document.getElementById("areaKuis");
    const layarAkhir = document.getElementById("layarAkhir");
    const btnMulai = document.getElementById("btnMulai");
    const btnLanjut = document.getElementById("btnLanjut");
    const btnUlang = document.getElementById("btnUlang");

    const namaManual = document.getElementById("namaManual");
    const kelasManual = document.getElementById("kelasManual");
    const pesanAwal = document.getElementById("pesanAwal");
    const statusSimpan = document.getElementById("statusSimpan");

    const nomorSoal = document.getElementById("nomorSoal");
    const nilaiSaatIni = document.getElementById("nilaiSaatIni");
    const waktuTeks = document.getElementById("waktuTeks");
    const barWaktu = document.getElementById("barWaktu");
    const pertanyaan = document.getElementById("pertanyaan");
    const pilihan = document.getElementById("pilihan");
    const umpanBalik = document.getElementById("umpanBalik");
    const ledakanEl = document.getElementById("ledakan");

    let soal = [];
    let indeks = 0;
    let benar = 0;
    let salah = 0;
    let lewat = 0;
    let sudahMenjawab = false;
    let timerInterval = null;
    let timeoutSoal = null;
    let waktuMulai = 0;

    let tokenPantau = "";
    let timerHeartbeat = null;
    let idSantriAktif = 0;
    let namaSantriAktif = "";
    let kelasSantriAktif = "";
    let kuisBerjalan = false;
    let hasilSudahDisimpan = false;
    let nilaiAkhirKuis = 0;

    /* =====================================================
       REQUEST POST KE FILE INI
    ===================================================== */
    async function kirimPost(data) {
        const form = new URLSearchParams();

        Object.keys(data).forEach(k => {
            form.append(k, String(data[k]));
        });

        const response = await fetch(
            window.location.pathname + window.location.search,
            {
                method: "POST",
                headers: {
                    "Content-Type":
                        "application/x-www-form-urlencoded; charset=UTF-8"
                },
                body: form.toString(),
                cache: "no-store",
                credentials: "same-origin"
            }
        );

        const teks = await response.text();
        let hasil;

        try {
            hasil = JSON.parse(teks);
        } catch (e) {
            throw new Error(
                "Respons server bukan JSON. Periksa error PHP dan config.php."
            );
        }

        if (!response.ok || !hasil.ok) {
            throw new Error(hasil.pesan || "Permintaan ke server gagal.");
        }

        return hasil;
    }

    /* =====================================================
       HEARTBEAT
    ===================================================== */
    function buatToken() {
        if (window.crypto && window.crypto.getRandomValues) {
            const bytes = new Uint8Array(32);
            window.crypto.getRandomValues(bytes);

            return Array.from(bytes)
                .map(b => b.toString(16).padStart(2, "0"))
                .join("");
        }

        return Array.from({ length: 64 }, () =>
            Math.floor(Math.random() * 16).toString(16)
        ).join("");
    }

    function jumlahDikerjakan() {
        return Math.min(JUMLAH_SOAL, benar + salah + lewat);
    }

    function nilaiSementara() {
        const dikerjakan = jumlahDikerjakan();

        if (dikerjakan <= 0) return 0;

        return Math.round((benar / dikerjakan) * 100);
    }

    async function kirimPantau(aksi) {
        if (!tokenPantau) return false;

        const data = {
            aksi_pantau: aksi,
            token: tokenPantau
        };

        if (aksi === "mulai") {
            data.nama_siswa = namaSantriAktif;
            data.kelas = kelasSantriAktif;
            data.total_soal = JUMLAH_SOAL;
        }

        if (aksi === "update" || aksi === "selesai") {
            data.soal_dikerjakan = jumlahDikerjakan();
            data.jumlah_benar = benar;
            data.nilai = aksi === "selesai"
                ? nilaiAkhirKuis
                : nilaiSementara();
        }

        try {
            await kirimPost(data);
            return true;
        } catch (error) {
            console.error("Heartbeat:", error.message);

            if (aksi === "mulai") {
                pesanAwal.textContent =
                    "Heartbeat gagal: " + error.message;
                pesanAwal.style.color = "#b91c1c";
            }

            return false;
        }
    }

    function mulaiHeartbeat() {
        clearInterval(timerHeartbeat);

        timerHeartbeat = setInterval(() => {
            if (kuisBerjalan && tokenPantau) {
                kirimPantau("ping");
            }
        }, INTERVAL_HEARTBEAT);
    }

    function hentikanHeartbeat() {
        clearInterval(timerHeartbeat);
        timerHeartbeat = null;
    }

    /* =====================================================
       ACAK ARRAY
    ===================================================== */
    function acak(array) {
        const hasil = array.slice();

        for (let i = hasil.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [hasil[i], hasil[j]] = [hasil[j], hasil[i]];
        }

        return hasil;
    }

    /* =====================================================
       PILIHAN JAWABAN
    ===================================================== */
    function pilihanJawaban(s) {
        return acak(s.pilihan);
    }

    /* =====================================================
       DROPDOWN NAMA SISWA
    ===================================================== */
    const btnDaftarNama = document.getElementById("btnDaftarNama");
    const daftarNamaKustom =
        document.getElementById("daftarNamaKustom");

    const opsiSemuaNama = daftarNamaKustom
        ? daftarNamaKustom.querySelectorAll(".opsi-nama")
        : [];

    if (btnDaftarNama && daftarNamaKustom) {
        btnDaftarNama.addEventListener("click", function(e) {
            e.preventDefault();
            e.stopPropagation();

            const akanDibuka = daftarNamaKustom.hidden;
            daftarNamaKustom.hidden = !akanDibuka;
            btnDaftarNama.textContent = akanDibuka ? "⌃" : "⌄";
            btnDaftarNama.setAttribute(
                "aria-expanded",
                String(akanDibuka)
            );
        });

        opsiSemuaNama.forEach(function(opsi) {
            opsi.addEventListener("click", function(e) {
                e.preventDefault();
                e.stopPropagation();

                namaManual.value = opsi.dataset.nama || "";
                kelasManual.value = opsi.dataset.kelas || "";
                idSantriAktif = parseInt(opsi.dataset.id, 10) || 0;

                daftarNamaKustom.hidden = true;
                btnDaftarNama.textContent = "⌄";
                btnDaftarNama.setAttribute("aria-expanded", "false");

                pesanAwal.textContent = "";
                pesanAwal.style.color = "";
            });
        });

        document.addEventListener("click", function(e) {
            if (!e.target.closest(".nama-wrapper")) {
                daftarNamaKustom.hidden = true;
                btnDaftarNama.textContent = "⌄";
                btnDaftarNama.setAttribute("aria-expanded", "false");
            }
        });
    }

    namaManual.addEventListener("input", function() {
        const namaKetik =
            namaManual.value.trim().toLocaleLowerCase("id");

        const namaCocok = Array.from(opsiSemuaNama).find(function(opsi) {
            return (opsi.dataset.nama || "")
                .trim()
                .toLocaleLowerCase("id") === namaKetik;
        });

        if (namaCocok && namaKetik !== "") {
            idSantriAktif = parseInt(namaCocok.dataset.id, 10) || 0;
            kelasManual.value = namaCocok.dataset.kelas || "";
        } else {
            idSantriAktif = 0;
        }
    });

    /* =====================================================
       MULAI KUIS
    ===================================================== */
    async function mulaiKuis() {
        if (kuisBerjalan || btnMulai.disabled) return;

        if (JUMLAH_SOAL < 1) {
            pesanAwal.textContent =
                "Tidak ada soal valid dengan nama = Laila. Periksa tabel soal dan kolom jawaban.";
            pesanAwal.style.color = "#b91c1c";
            return;
        }

        namaSantriAktif = namaManual.value.trim();
        kelasSantriAktif = kelasManual.value.trim();

        if (!namaSantriAktif) {
            pesanAwal.textContent =
                "Silakan pilih atau ketik nama siswa.";
            pesanAwal.style.color = "#b91c1c";
            namaManual.focus();
            return;
        }

        if (!kelasSantriAktif) {
            pesanAwal.textContent =
                "Silakan pilih atau ketik kelas siswa.";
            pesanAwal.style.color = "#b91c1c";
            kelasManual.focus();
            return;
        }

        btnMulai.disabled = true;
        btnMulai.textContent = "Memulai kuis...";
        btnUlang.disabled = true;

        pesanAwal.textContent = "Menyiapkan kuis dan pemantauan...";
        pesanAwal.style.color = "#64748b";

        try {
            clearInterval(timerInterval);
            clearTimeout(timeoutSoal);
            hentikanHeartbeat();

            tokenPantau = buatToken();
            hasilSudahDisimpan = false;
            nilaiAkhirKuis = 0;

            const mulaiBerhasil = await kirimPantau("mulai");

            if (!mulaiBerhasil) {
                throw new Error(
                    "Heartbeat gagal dimulai. Periksa tabel madin_heartbeat."
                );
            }

            soal = acak(BANK_SOAL_DB).slice(0, JUMLAH_SOAL);

            indeks = 0;
            benar = 0;
            salah = 0;
            lewat = 0;
            sudahMenjawab = false;
            kuisBerjalan = true;

            layarAwal.hidden = true;
            layarAkhir.hidden = true;
            areaKuis.style.display = "block";

            mulaiHeartbeat();
            tampilkanSoal();

        } catch (error) {
            console.error("Gagal memulai kuis:", error);

            kuisBerjalan = false;
            hentikanHeartbeat();

            pesanAwal.textContent =
                "Kuis gagal dimulai: " + error.message;
            pesanAwal.style.color = "#b91c1c";

        } finally {
            if (!kuisBerjalan) {
                btnMulai.disabled = false;
                btnMulai.textContent = "Mulai Kuis";
            }

            btnUlang.disabled = false;
        }
    }

    /* =====================================================
       TAMPILKAN SOAL
    ===================================================== */
    function tampilkanSoal() {
        clearInterval(timerInterval);
        clearTimeout(timeoutSoal);

        if (indeks >= JUMLAH_SOAL) {
            akhiriKuis();
            return;
        }

        sudahMenjawab = false;

        const s = soal[indeks];
        const waktuPerSoal = Math.max(
    1,
    Number.parseInt(s.waktu_per_soal, 10) || 7
);

        nomorSoal.textContent =
            `Soal ${indeks + 1} dari ${JUMLAH_SOAL}`;

        nilaiSaatIni.textContent = `Benar: ${benar}`;
        pertanyaan.classList.remove("animasi-masuk");
        void pertanyaan.offsetWidth;
        pertanyaan.classList.add("animasi-masuk");
        pertanyaan.textContent = s.teks;
        umpanBalik.textContent = "";
        umpanBalik.style.color = "";
        btnLanjut.hidden = true;
        pilihan.innerHTML = "";

        const opsi = pilihanJawaban(s);

        opsi.forEach((teks, i) => {
            const tombol = document.createElement("button");

            tombol.type = "button";
            tombol.className = "btn-pilihan animasi-masuk";
            tombol.style.animationDelay = `${i * 70}ms`;
            tombol.textContent =
                `${String.fromCharCode(65 + i)}. ${teks}`;

            tombol.addEventListener("click", () => {
                periksaJawaban(teks, tombol);
            });

            pilihan.appendChild(tombol);
        });

        waktuMulai = Date.now();
        perbaruiWaktu();

        timerInterval = setInterval(perbaruiWaktu, 100);

        timeoutSoal = setTimeout(() => {
    if (sudahMenjawab) return;

    sudahMenjawab = true;
    lewat++;

    clearInterval(timerInterval);
    kunciPilihan();

    umpanBalik.textContent =
        `Waktu habis! Jawaban yang benar: ${s.jawaban}`;
    umpanBalik.style.color = "#b45309";

    tandaiJawabanBenar(String(s.jawaban));
    ledakan();
    hancurkanElemenEfek();
    kirimPantau("update");

    selesaiMenjawab();

}, waktuPerSoal * 1000);
    }

    /* =====================================================
       TIMER VISUAL
       MENGGUNAKAN WAKTU DARI PENGATURAN PHP
    ===================================================== */
function perbaruiWaktu() {
    if (indeks >= JUMLAH_SOAL) return;

    const s = soal[indeks];

    const waktuPerSoal = Math.max(
        1,
        Number.parseInt(s.waktu_per_soal, 10) || 7
    );

    const sisa = Math.max(
        0,
        waktuPerSoal - (Date.now() - waktuMulai) / 1000
    );

    const persen = (sisa / waktuPerSoal) * 100;

    barWaktu.style.width = `${persen}%`;

    barWaktu.style.background =
        sisa <= waktuPerSoal / 3 ? "#dc2626" :
        sisa <= waktuPerSoal * 2 / 3 ? "#f59e0b" :
        "#16a34a";

    waktuTeks.textContent =
        `Waktu: ${sisa.toFixed(1)} detik`;
}

    /* =====================================================
       NONAKTIFKAN PILIHAN
    ===================================================== */
    function kunciPilihan() {
        pilihan.querySelectorAll("button").forEach(t => {
            t.disabled = true;
        });
    }

    function tandaiJawabanBenar(teksBenar) {
        pilihan.querySelectorAll("button").forEach(t => {
            const teksTombol = t.textContent
                .replace(/^[A-F]\.\s*/, "")
                .trim();

            if (teksTombol === teksBenar) {
                t.classList.add("benar");
            }
        });
    }

    /* =====================================================
       EFEK LEDAKAN DAN KEMBANG API
       Hanya untuk animasi; tidak mengubah proses penilaian.
    ===================================================== */
    function ledakan() {
        if (!ledakanEl) return;

        ledakanEl.classList.remove("active");
        void ledakanEl.offsetWidth;
        ledakanEl.classList.add("active");

        window.setTimeout(() => {
            ledakanEl.classList.remove("active");
        }, 800);
    }

    function hancurkanElemenEfek() {
        const target = [
            pertanyaan,
            ...Array.from(pilihan.querySelectorAll(".btn-pilihan"))
        ];

        target.forEach(el => {
            const x = (Math.random() - 0.5) * 900;
            const y = (Math.random() - 0.5) * 500;
            const r = (Math.random() - 0.5) * 100;

            el.style.setProperty("--x", `${x}px`);
            el.style.setProperty("--y", `${y}px`);
            el.style.setProperty("--r", `${r}deg`);
            el.classList.add("hancur");
        });

        window.setTimeout(() => {
            target.forEach(el => {
                el.classList.remove("hancur");
                el.style.removeProperty("--x");
                el.style.removeProperty("--y");
                el.style.removeProperty("--r");
            });
        }, 800);
    }

    function kembangApi() {
        for (let i = 0; i < 5; i++) {
            window.setTimeout(buatKembangApi, i * 150);
        }
    }

    function buatKembangApi() {
        const x = 15 + Math.random() * 70;
        const y = 15 + Math.random() * 55;
        const jumlah = 35;

        for (let i = 0; i < jumlah; i++) {
            const partikel = document.createElement("div");
            partikel.className = "kembang-api";
            partikel.style.left = `${x}%`;
            partikel.style.top = `${y}%`;

            const hue = Math.floor(Math.random() * 360);
            partikel.style.background = `hsl(${hue}, 100%, 60%)`;
            partikel.style.boxShadow = `0 0 10px hsl(${hue}, 100%, 70%)`;
            document.body.appendChild(partikel);

            const sudut = (Math.PI * 2 * i) / jumlah;
            const jarak = 50 + Math.random() * 100;
            const tx = Math.cos(sudut) * jarak;
            const ty = Math.sin(sudut) * jarak;

            const animasi = partikel.animate(
                [
                    {
                        transform: "translate(-50%, -50%) scale(1)",
                        opacity: 1
                    },
                    {
                        transform: `translate(calc(-50% + ${tx}px), calc(-50% + ${ty}px)) scale(.1)`,
                        opacity: 0
                    }
                ],
                {
                    duration: 700 + Math.random() * 500,
                    easing: "ease-out"
                }
            );

            animasi.onfinish = () => partikel.remove();
        }
    }

    /* =====================================================
       PERIKSA JAWABAN
    ===================================================== */
    function periksaJawaban(teks, tombolDipilih) {
        if (sudahMenjawab || !kuisBerjalan) return;

        sudahMenjawab = true;

        clearInterval(timerInterval);
        clearTimeout(timeoutSoal);
        kunciPilihan();

        const s = soal[indeks];
        const benarStr = String(s.jawaban);
        const pilihStr = String(teks);
        const cocok = pilihStr === benarStr;

        if (cocok) {
            benar++;
            tombolDipilih.classList.add("benar");
            kembangApi();

            umpanBalik.textContent = "✓ Benar!";
            umpanBalik.style.color = "#15803d";
        } else {
            salah++;
            tombolDipilih.classList.add("salah");

            umpanBalik.textContent =
                `✗ Salah. Jawaban yang benar: ${benarStr}`;
            umpanBalik.style.color = "#b91c1c";

            tandaiJawabanBenar(benarStr);
            ledakan();
            hancurkanElemenEfek();
        }

        nilaiSaatIni.textContent = `Benar: ${benar}`;

        kirimPantau("update");
        selesaiMenjawab();
    }

    /* =====================================================
       OTOMATIS MENUJU SOAL BERIKUTNYA
    ===================================================== */
    function selesaiMenjawab() {
        clearInterval(timerInterval);
        clearTimeout(timeoutSoal);
        kunciPilihan();

        btnLanjut.hidden = true;

        timeoutSoal = setTimeout(() => {
            indeks++;
            tampilkanSoal();
        }, JEDA_SOAL_BERIKUTNYA);
    }

    /* =====================================================
       SIMPAN NILAI AKHIR
    ===================================================== */
    async function simpanNilaiAkhir() {
        if (hasilSudahDisimpan) return;

        hasilSudahDisimpan = true;
        statusSimpan.textContent = "Menyimpan nilai...";
        statusSimpan.style.color = "#64748b";

        try {
            await kirimPost({
                aksi: "simpan_nilai",
                id_santri: idSantriAktif,
                nama_siswa: namaSantriAktif,
                kelas: kelasSantriAktif,
                nilai: nilaiAkhirKuis
            });

            statusSimpan.textContent =
                `Nilai ${nilaiAkhirKuis} berhasil disimpan untuk ${namaSantriAktif} (${kelasSantriAktif}).`;

            statusSimpan.style.color = "#15803d";

        } catch (error) {
            hasilSudahDisimpan = false;

            statusSimpan.textContent =
                "Nilai belum berhasil disimpan: " + error.message;

            statusSimpan.style.color = "#b91c1c";
        }
    }

    /* =====================================================
       HASIL AKHIR
    ===================================================== */
    async function akhiriKuis() {
        clearInterval(timerInterval);
        clearTimeout(timeoutSoal);

        kuisBerjalan = false;
        hentikanHeartbeat();

        areaKuis.style.display = "none";
        layarAkhir.hidden = false;

        nilaiAkhirKuis = JUMLAH_SOAL > 0
            ? Math.round((benar / JUMLAH_SOAL) * 100)
            : 0;

        document.getElementById("hasilNilai").textContent =
            nilaiAkhirKuis;

        document.getElementById("hasilBenar").textContent = benar;
        document.getElementById("hasilSalah").textContent = salah;
        document.getElementById("hasilLewat").textContent = lewat;

        document.getElementById("pesanAkhir").textContent =
            nilaiAkhirKuis >= 85
                ? "Hebat! Penguasaan materi Anda sangat baik."
                : nilaiAkhirKuis >= 70
                ? "Bagus! Terus berlatih agar semakin teliti."
                : nilaiAkhirKuis >= 50
                ? "Cukup baik. Ulangi latihan untuk meningkatkan hasil."
                : "Tetap semangat. Berlatih sedikit demi sedikit akan membantu!";

        await kirimPantau("selesai");
        await simpanNilaiAkhir();

        btnUlang.disabled = false;
    }

    /* =====================================================
       EVENT TOMBOL
    ===================================================== */
    btnMulai.addEventListener("click", mulaiKuis);

    btnUlang.addEventListener("click", () => {
        layarAkhir.hidden = true;
        layarAwal.hidden = false;
        areaKuis.style.display = "none";

        statusSimpan.textContent = "";
        pesanAwal.textContent = "";

        btnMulai.disabled = false;
        btnMulai.textContent = "Mulai Kuis";
        btnUlang.disabled = false;

        kuisBerjalan = false;
        hentikanHeartbeat();
    });

    btnLanjut.addEventListener("click", () => {});
})();
</script>


</body>
</html>
