<?php
declare(strict_types=1);

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

$isPreview = (int)($_GET['preview'] ?? 0) === 1;

if ($isPreview) {
    $teacherLoggedIn = !empty($_SESSION['user_id'])
        && in_array(
            (string)($_SESSION['user_role'] ?? ''),
            ['teacher', 'admin'],
            true
        );

    if (!$teacherLoggedIn) {
        $_SESSION['teacher_return_url'] =
            (string)($_SERVER['REQUEST_URI'] ?? '');
        header('Location: ../teacher/login.php');
        exit;
    }
} elseif (empty($_SESSION['student_id'])) {
    $_SESSION['student_return_url'] =
        (string)($_SERVER['REQUEST_URI'] ?? '');
    header('Location: student-login.php');
    exit;
}

require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../includes/student_activity_navigation.php';

$pdo = getDbConnection();

$bookSlug = trim((string)($_GET['book'] ?? ''));
$levelSlug = trim((string)($_GET['level'] ?? ''));
$levelId = max(0, (int)($_GET['level_id'] ?? 0));
$unitId = max(0, (int)($_GET['unit_id'] ?? ($_GET['id'] ?? 0)));
$lessonId = max(0, (int)($_GET['lesson_id'] ?? 0));
$activityId = max(
    0,
    (int)($_GET['activity_id'] ?? ($_GET['activity'] ?? 0))
);

if (
    $bookSlug === ''
    || $levelId <= 0
    || $unitId <= 0
    || $lessonId <= 0
    || $activityId <= 0
) {
    header('Location: student-learning.php');
    exit;
}

function swbColumns(PDO $pdo, string $table): array
{
    $stmt = $pdo->prepare(
        "SELECT COUNT(*)
         FROM information_schema.TABLES
         WHERE TABLE_SCHEMA = DATABASE()
           AND TABLE_NAME = ?"
    );
    $stmt->execute([$table]);

    if ((int)$stmt->fetchColumn() === 0) {
        return [];
    }

    $rows = $pdo
        ->query("SHOW COLUMNS FROM `{$table}`")
        ->fetchAll(PDO::FETCH_ASSOC);

    return array_column($rows, null, 'Field');
}

function swbAssetUrl(string $path): string
{
    $path = trim($path);

    if ($path === '') {
        return '';
    }

    if (
        str_starts_with($path, 'http://')
        || str_starts_with($path, 'https://')
        || str_starts_with($path, 'data:')
        || str_starts_with($path, '/')
    ) {
        return $path;
    }

    return '/EnglishSpeak/' . ltrim($path, './');
}

function swbFirstColumn(array $columns, array $candidates): ?string
{
    foreach ($candidates as $candidate) {
        if (isset($columns[$candidate])) {
            return $candidate;
        }
    }

    return null;
}

$activityColumns = swbColumns($pdo, 'activities');
$relationColumns = swbColumns($pdo, 'flashcard_set_words');
$wordColumns = swbColumns($pdo, 'words');

if (
    $activityColumns === []
    || $relationColumns === []
    || $wordColumns === []
) {
    http_response_code(500);
    exit('Thiếu bảng dữ liệu Word Unscramble.');
}

$activityTitleColumn = swbFirstColumn(
    $activityColumns,
    ['title', 'name']
);

$activitySetColumn = swbFirstColumn(
    $activityColumns,
    ['flashcard_set_id', 'set_id']
);

$setFk = swbFirstColumn(
    $relationColumns,
    ['set_id', 'flashcard_set_id']
);

$wordFk = swbFirstColumn(
    $relationColumns,
    ['word_id', 'item_id']
);

$orderColumn = swbFirstColumn(
    $relationColumns,
    ['order_no', 'sort_order', 'position', 'id']
) ?? 'id';

$wordTextColumn = swbFirstColumn(
    $wordColumns,
    ['word', 'english_word', 'term', 'title', 'name']
);

$imageColumn = swbFirstColumn(
    $wordColumns,
    ['image_path', 'image', 'image_url', 'picture_path']
);

$audioColumn = swbFirstColumn(
    $wordColumns,
    ['audio_us_path', 'audio_path', 'audio', 'audio_url']
);

if (
    $activityTitleColumn === null
    || $activitySetColumn === null
    || $setFk === null
    || $wordFk === null
    || $wordTextColumn === null
) {
    http_response_code(500);
    exit('Cấu trúc dữ liệu Word Unscramble chưa đầy đủ.');
}

$activityStatusWhere = isset($activityColumns['status'])
    ? "AND a.status = 'active'"
    : '';

$contextStmt = $pdo->prepare(
    "SELECT
        a.id AS activity_id,
        a.`{$activitySetColumn}` AS flashcard_set_id,
        "
        . (
            isset($activityColumns['xp_reward'])
                ? 'a.xp_reward'
                : '10 AS xp_reward'
        )
        . ",
        a.`{$activityTitleColumn}` AS activity_title,

        le.id AS lesson_id,
        le.lesson_number,
        le.title AS lesson_title,

        u.id AS unit_id,
        u.unit_number,
        u.title AS unit_title,

        l.id AS level_id,
        l.name AS level_name,
        l.slug AS level_slug,

        b.id AS book_id,
        b.name AS book_name,
        b.slug AS book_slug
     FROM activities a
     INNER JOIN lessons le
        ON le.id = a.lesson_id
     INNER JOIN units u
        ON u.id = le.unit_id
     INNER JOIN levels l
        ON l.id = u.level_id
     INNER JOIN books b
        ON b.id = l.book_id
     WHERE a.id = ?
       AND le.id = ?
       AND u.id = ?
       AND l.id = ?
       AND b.slug = ?
       {$activityStatusWhere}
     LIMIT 1"
);
$contextStmt->execute([
    $activityId,
    $lessonId,
    $unitId,
    $levelId,
    $bookSlug,
]);
$activity = $contextStmt->fetch(PDO::FETCH_ASSOC);

if (!$activity) {
    http_response_code(404);
    exit('Không tìm thấy Word Unscramble trong cấp độ đã chọn.');
}

$runtimeSettings = [
    'word_count' => 6,
    'show_image' => true,
    'letter_voice' => true,
    'passing_score' => 80,
];

if (swbColumns($pdo, 'activity_settings') !== []) {
    $settingsStmt = $pdo->prepare(
        'SELECT settings_json
         FROM activity_settings
         WHERE activity_id = ?
         LIMIT 1'
    );
    $settingsStmt->execute([$activityId]);
    $settingsJson = $settingsStmt->fetchColumn();

    if (is_string($settingsJson) && $settingsJson !== '') {
        $decoded = json_decode($settingsJson, true);

        if (is_array($decoded)) {
            $runtimeSettings['word_count'] = max(
                1,
                min(20, (int)($decoded['word_count'] ?? 6))
            );
            $runtimeSettings['show_image'] = filter_var(
                $decoded['show_image'] ?? true,
                FILTER_VALIDATE_BOOL
            );
            $runtimeSettings['letter_voice'] = filter_var(
                $decoded['letter_voice'] ?? true,
                FILTER_VALIDATE_BOOL
            );
            $runtimeSettings['passing_score'] = max(
                0,
                min(100, (int)($decoded['passing_score'] ?? 80))
            );
        }
    }
}

$imageSelect = $imageColumn !== null
    ? "w.`{$imageColumn}` AS image_path"
    : "'' AS image_path";

$audioSelect = $audioColumn !== null
    ? "w.`{$audioColumn}` AS audio_path"
    : "'' AS audio_path";

$wordStatusWhere = isset($wordColumns['status'])
    ? "AND w.status = 'active'"
    : '';

$wordStmt = $pdo->prepare(
    "SELECT
        w.id,
        w.`{$wordTextColumn}` AS word_text,
        {$imageSelect},
        {$audioSelect},
        fsw.`{$orderColumn}` AS display_order
     FROM flashcard_set_words fsw
     INNER JOIN words w
        ON w.id = fsw.`{$wordFk}`
     WHERE fsw.`{$setFk}` = ?
       {$wordStatusWhere}
     ORDER BY fsw.`{$orderColumn}` ASC, fsw.id ASC
     LIMIT " . (int)$runtimeSettings['word_count']
);
$wordStmt->execute([
    (int)$activity['flashcard_set_id'],
]);

$rows = $wordStmt->fetchAll(PDO::FETCH_ASSOC);

$cards = [];

foreach ($rows as $row) {
    $word = strtoupper(
        preg_replace(
            '/[^A-Za-z]/',
            '',
            (string)($row['word_text'] ?? '')
        )
    );

    if ($word === '') {
        continue;
    }

    $cards[] = [
        'word' => $word,
        'display_word' => trim((string)($row['word_text'] ?? '')),
        'image' => swbAssetUrl((string)($row['image_path'] ?? '')),
        'audio' => swbAssetUrl((string)($row['audio_path'] ?? '')),
    ];
}

$rawUnitNumber = trim((string)$activity['unit_number']);
$rawLessonNumber = trim((string)$activity['lesson_number']);

$displayUnitNumber = preg_match('/(\d+)/', $rawUnitNumber, $unitMatch)
    ? (int)$unitMatch[1]
    : $rawUnitNumber;

$displayLessonNumber = preg_match('/(\d+)/', $rawLessonNumber, $lessonMatch)
    ? (int)$lessonMatch[1]
    : $rawLessonNumber;

$backUrl = $isPreview
    ? '../teacher/activity-preview/index.php?activity_id=' . (int)$activityId
    : 'student-activities.php?' . http_build_query([
        'book' => (string)$activity['book_slug'],
        'level' => (string)$activity['level_slug'],
        'level_id' => (int)$activity['level_id'],
        'unit_id' => (int)$activity['unit_id'],
        'lesson_id' => (int)$activity['lesson_id'],
        'lesson' => $displayLessonNumber,
    ]);

$nextActivityUrl = englishSpeakGetNextActivityUrl($pdo, $activityId);

$_SESSION['last_learning_url'] =
    (string)($_SERVER['REQUEST_URI'] ?? '');
?>
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>Word Unscramble | EnglishSpeak</title>
<style>
:root{
  --blue:#198fe3;--blue-dark:#0868c7;--page:#f4f8fc;
  --text:#15314b;--muted:#718399;--line:#dfe8f0;
  --green:#26a65b;--orange:#f39c12;--width:520px;
  --shadow:0 7px 22px rgba(31,71,108,.10)
}
*{box-sizing:border-box}
html,body{margin:0;min-height:100%;background:var(--page);color:var(--text);font-family:"Segoe UI",Tahoma,Arial,sans-serif}
body{display:flex;justify-content:center}
button,a{font:inherit;-webkit-tap-highlight-color:transparent}
.app{width:min(100%,var(--width));min-height:100dvh;padding:0 13px 24px;background:#fff;box-shadow:0 0 28px rgba(30,79,115,.08)}
.topbar{position:sticky;top:0;z-index:40;display:grid;grid-template-columns:46px minmax(0,1fr) 46px;align-items:center;gap:9px;min-height:76px;margin:0 -13px 14px;padding:12px 13px;background:linear-gradient(135deg,#3488ef,#17b8e4);color:#fff;box-shadow:0 5px 15px rgba(31,135,210,.22)}
.round{display:grid;width:44px;height:44px;place-items:center;border:0;border-radius:50%;background:rgba(255,255,255,.15);color:#fff;text-decoration:none;font-size:24px;font-weight:850}
.heading{text-align:center;min-width:0}.heading strong{display:block;font-size:22px}.heading small{display:block;margin-top:4px;color:rgba(255,255,255,.92);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lesson{display:grid;grid-template-columns:52px minmax(0,1fr);align-items:center;gap:11px;min-height:72px;margin-bottom:12px;padding:12px 14px;border-radius:20px;background:linear-gradient(135deg,#edf6ff,#e6f1ff)}
.lesson__icon{width:48px;height:48px;display:grid;place-items:center;border-radius:14px;background:#fff;font-size:29px}.lesson strong{font-size:18px}
.progress-row{display:flex;justify-content:space-between;gap:10px;color:#38536c;font-size:13px;font-weight:800}.track{height:9px;margin:8px 0 13px;overflow:hidden;border-radius:999px;background:#dceaf5}.fill{width:0;height:100%;border-radius:inherit;background:linear-gradient(90deg,#1c7ce4,#18a9e7);transition:width .2s}
.game-card{min-height:calc(100dvh - 292px);padding:15px;border:1px solid #e2eaf1;border-radius:22px;background:#fff;box-shadow:var(--shadow);display:flex;flex-direction:column;align-items:center}
.image-box{width:100%;height:min(28dvh,225px);min-height:160px;display:grid;place-items:center;padding:18px;border:0;border-radius:18px;background:#fbfdff;cursor:pointer}
.image-box img{max-width:250px;max-height:220px;width:88%;height:88%;object-fit:contain}.fallback{font-size:96px}
.unscramble-help{margin:14px 0 10px;color:var(--muted);font-size:13px;font-weight:750;text-align:center}
.unscramble-row{display:flex;justify-content:center;flex-wrap:wrap;gap:9px;width:100%;min-height:72px;margin-top:4px}
.unscramble-tile{position:relative;min-width:62px;height:62px;padding:0 14px;border:2px solid #c7dceb;border-radius:15px;background:#fff;color:var(--blue-dark);font-size:26px;font-weight:900;box-shadow:0 5px 14px rgba(32,88,128,.09);cursor:grab;touch-action:none;user-select:none;transition:.16s}
.unscramble-tile:active{cursor:grabbing}
.unscramble-tile.dragging{
  opacity:.9;
  transform:translate(var(--drag-x,0px),var(--drag-y,0px)) scale(1.08);
  z-index:50;
  position:relative;
  pointer-events:none;
}
.unscramble-tile.drag-over{border-color:var(--blue);background:#eaf5ff;transform:translateY(-3px)}
.unscramble-tile.correct{border-color:#77d59a;background:#eefbf2;color:#16823b;animation:slotSuccess .35s ease}
.unscramble-tile.correct::after{content:'✓';position:absolute;right:-6px;top:-8px;display:grid;width:22px;height:22px;place-items:center;border-radius:50%;background:#27ae60;color:#fff;font-size:13px;font-weight:900;box-shadow:0 3px 8px rgba(39,174,96,.25)}
@keyframes slotSuccess{0%{transform:scale(.96)}60%{transform:scale(1.08)}100%{transform:scale(1)}}
.controls{display:grid;grid-template-columns:1fr 1fr;gap:10px;width:100%;margin-top:auto;padding-top:20px}
.control{height:52px;border:1px solid #cbddeb;border-radius:15px;background:#fff;color:var(--text);font-weight:850;cursor:pointer}.control.primary{border-color:var(--blue);background:var(--blue);color:#fff}
.feedback{display:none;width:100%;margin-top:12px;padding:13px 14px;border-radius:15px;font-weight:800;text-align:center}.feedback.show{display:block}.feedback.good{border:1px solid #bce5c8;background:#eefbf2;color:#19783a}.feedback.try{border:1px solid #f0d7a5;background:#fff8e8;color:#8a5c08}
.next-row{display:none;width:100%;margin-top:10px}.next-row.show{display:grid}.next{height:52px;border:0;border-radius:15px;background:var(--blue);color:#fff;font-weight:850;cursor:pointer}
.finish{display:none;min-height:calc(100dvh - 110px);padding:38px 18px;align-items:center;justify-content:center;flex-direction:column;text-align:center}.finish.show{display:flex}.finish__icon{font-size:76px}.finish h2{margin:14px 0 8px;font-size:40px;color:#123e62}.finish p{margin:0;color:var(--muted)}
.summary{width:100%;margin-top:22px;padding:5px 17px;border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow)}.summary-row{display:flex;justify-content:space-between;align-items:center;min-height:58px;border-bottom:1px solid #e5ecf2}.summary-row:last-child{border:0}.summary-row strong{color:var(--blue-dark);font-size:19px}
.finish-actions{display:grid;gap:10px;width:100%;margin-top:18px}.finish-actions button,.finish-actions a{min-height:54px;display:grid;place-items:center;border:1px solid var(--blue);border-radius:15px;background:#fff;color:var(--blue-dark);text-decoration:none;font-weight:850}.finish-actions .primary{background:var(--blue);color:#fff}
.preview-banner{margin:10px 0;padding:9px 12px;border:1px solid #bfdbfe;border-radius:13px;background:#eff6ff;color:#1d4ed8;text-align:center;font-size:13px;font-weight:800}
.empty{padding:50px 20px;text-align:center}
@media(max-width:390px){.app{padding-left:10px;padding-right:10px}.topbar{margin-left:-10px;margin-right:-10px;padding-left:10px;padding-right:10px}.unscramble-tile{min-width:52px;height:54px;font-size:23px}.game-card{padding:12px}}

.xp-popup{position:fixed;z-index:10000;inset:0;display:none;align-items:center;justify-content:center;padding:20px;background:rgba(18,49,74,.38);backdrop-filter:blur(5px)}
.xp-popup.show{display:flex}
.xp-popup__card{width:min(100%,350px);padding:24px 20px 20px;border-radius:24px;background:#fff;box-shadow:0 18px 50px rgba(18,49,74,.24);text-align:center}
.xp-popup__icon{width:76px;height:76px;display:grid;place-items:center;margin:0 auto 10px;border-radius:50%;background:linear-gradient(135deg,#fff6bf,#ffe06e);font-size:42px}
.xp-popup__title{margin:0;color:#15314b;font-size:27px;line-height:1.2}
.xp-popup__text{margin:7px 0 0;color:#718399;font-size:14px;line-height:1.5}
.xp-popup__score{margin:17px 0;color:#f39c12;font-size:36px;font-weight:900;line-height:1}
.xp-popup__button{width:100%;min-height:52px;border:0;border-radius:15px;background:linear-gradient(135deg,#237fe1,#159ddd);color:#fff;font-weight:850;cursor:pointer}

</style>
</head>
<body>
<main class="app">
<?php if ($isPreview): ?>
<div class="preview-banner">Preview mode — Không lưu XP hoặc tiến trình học sinh.</div>
<?php endif; ?>

<header class="topbar">
  <a class="round" href="<?= htmlspecialchars($backUrl, ENT_QUOTES, 'UTF-8') ?>" aria-label="Quay lại">←</a>
  <div class="heading">
    <strong>Unit <?= $displayUnitNumber ?></strong>
    <small><?= htmlspecialchars((string)$activity['unit_title'], ENT_QUOTES, 'UTF-8') ?></small>
  </div>
  <span class="round" aria-hidden="true" style="visibility:hidden"></span>
</header>

<?php if ($cards === []): ?>
<section class="empty">
  <h2>Chưa có từ vựng</h2>
  <p>Hãy liên kết Flashcard Set trong Activity Settings.</p>
</section>
<?php else: ?>
<section id="gameScreen">
  <div class="lesson">
    <div class="lesson__icon">🔀</div>
    <strong>Word Unscramble</strong>
  </div>

  <div class="progress-row">
    <span id="progressLabel">1 / <?= count($cards) ?></span>
    <span id="scoreLabel"></span>
  </div>
  <div class="track"><div class="fill" id="progressFill"></div></div>

  <section class="game-card">
    <button class="image-box" id="imageButton" type="button" aria-label="Listen" <?= !$runtimeSettings['show_image'] ? 'hidden' : '' ?>>
      <img id="wordImage" alt="" hidden>
      <span class="fallback" id="fallback">🔤</span>
    </button>

    <p class="unscramble-help">Kéo chữ tới vị trí mới.</p>
    <div class="unscramble-row" id="unscrambleRow" aria-label="Shuffled letters"></div>


    <div class="feedback" id="feedback" aria-live="polite"></div>

    <div class="next-row" id="nextRow">
      <button class="next" id="nextButton" type="button">Next →</button>
    </div>
  </section>
</section>

<section class="finish" id="finishScreen">
  <div class="finish__icon">🎉</div>
  <h2 id="finishTitle">Great!</h2>
  <p id="finishSubtitle">All done!</p>

  <div class="summary">
    <div class="summary-row"><span>Done</span><strong><?= count($cards) ?>/<?= count($cards) ?></strong></div>
    <div class="summary-row"><span>Score</span><strong id="finalScore">0/100</strong></div>
    <div class="summary-row"><span>Time</span><strong id="finalTime">00:00</strong></div>
  </div>

  <div class="finish-actions">
    <a
      class="primary"
      id="continueButton"
      style="display:none"
      href="<?= htmlspecialchars($nextActivityUrl, ENT_QUOTES, 'UTF-8') ?>"
    >→ Next</a>
    <button id="againButton" type="button">↻ Again</button>
    <a href="<?= htmlspecialchars($backUrl, ENT_QUOTES, 'UTF-8') ?>">← Back</a>
  </div>
</section>
<?php endif; ?>
</main>


<div class="xp-popup" id="xpPopup" role="dialog" aria-modal="true" aria-labelledby="xpPopupTitle">
  <div class="xp-popup__card">
    <div class="xp-popup__icon" aria-hidden="true">⭐</div>
    <h2 class="xp-popup__title" id="xpPopupTitle">Great!</h2>
    <p class="xp-popup__text" id="xpPopupText">You earned</p>
    <div class="xp-popup__score" id="xpPopupScore"></div>
    <button class="xp-popup__button" id="xpPopupButton" type="button">Hiện kết quả</button>
  </div>
</div>

<?php if ($cards !== []): ?>
<script>
'use strict';

const isPreview = <?= $isPreview ? 'true' : 'false' ?>;
const activityId = <?= (int)$activityId ?>;
const settings = <?= json_encode(
    $runtimeSettings,
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
) ?>;
const cards = <?= json_encode(
    $cards,
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
) ?>;

const $ = id => document.getElementById(id);

const gameScreen = $('gameScreen');
const finishScreen = $('finishScreen');
const wordImage = $('wordImage');
const fallback = $('fallback');
const unscrambleRow = $('unscrambleRow');
const progressLabel = $('progressLabel');
const progressFill = $('progressFill');
const feedback = $('feedback');
const nextRow = $('nextRow');
const finalScore = $('finalScore');
const finalTime = $('finalTime');
const finishTitle = $('finishTitle');
const finishSubtitle = $('finishSubtitle');
const xpPopup = $('xpPopup');
const xpPopupTitle = $('xpPopupTitle');
const xpPopupText = $('xpPopupText');
const xpPopupScore = $('xpPopupScore');
const xpPopupButton = $('xpPopupButton');
const continueButton = $('continueButton');

let currentIndex = 0;
let currentLetters = [];
let dragIndex = null;
let dragStartX = 0;
let dragStartY = 0;
let dragButton = null;
let hasMovedLetter = false;
let correctCount = 0;
let mistakes = 0;
let startedAt = Date.now();
let saving = false;
let wordAudioTimer = null;
let autoNextTimer = null;
let wordCompleted = false;

function shuffle(items) {
    const copy = [...items];

    for (let i = copy.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [copy[i], copy[j]] = [copy[j], copy[i]];
    }

    return copy;
}

function speakText(text, rate = 0.72) {
    if (!('speechSynthesis' in window)) {
        return;
    }

    window.speechSynthesis.cancel();

    const utterance = new SpeechSynthesisUtterance(text);
    utterance.lang = 'en-US';
    utterance.rate = rate;
    utterance.pitch = 1;

    window.speechSynthesis.speak(utterance);
}

function playWord(card) {
    if (card.audio) {
        const audio = new Audio(card.audio);
        audio.play().catch(() => speakText(card.display_word));
        return;
    }

    speakText(card.display_word);
}

function playLetter(letter) {
    if (!settings.letter_voice) {
        return;
    }

    speakText(letter, 0.78);
}

function clearFeedback() {
    feedback.className = 'feedback';
    feedback.textContent = '';
}

function showFeedback(type, message) {
    feedback.className = `feedback show ${type}`;
    feedback.textContent = message;
}

function isLetterCorrect(index) {
    return currentLetters[index]?.letter === cards[currentIndex].word[index];
}

function renderLetters() {
    unscrambleRow.innerHTML = '';

    currentLetters.forEach((item, index) => {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'unscramble-tile';
        button.textContent = item.letter;
        button.draggable = false;

        if (hasMovedLetter && isLetterCorrect(index)) {
            button.classList.add('correct');
        }

        button.addEventListener('dragstart', event => {
            if (wordCompleted) return;

            dragIndex = index;
            button.classList.add('dragging');
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('text/plain', String(index));
        });

        button.addEventListener('dragend', () => {
            dragIndex = null;
            button.classList.remove('dragging');

            document.querySelectorAll('.unscramble-tile').forEach(tile => {
                tile.classList.remove('drag-over');
            });
        });

        button.addEventListener('dragover', event => {
            if (wordCompleted) return;
            event.preventDefault();
            button.classList.add('drag-over');
        });

        button.addEventListener('dragleave', () => {
            button.classList.remove('drag-over');
        });

        button.addEventListener('drop', event => {
            if (wordCompleted) return;

            event.preventDefault();
            button.classList.remove('drag-over');

            const fromIndex = Number(
                event.dataTransfer.getData('text/plain')
            );

            moveLetter(fromIndex, index);
        });

        button.addEventListener('pointerdown', event => {
            if (wordCompleted) {
                return;
            }

            event.preventDefault();
            dragIndex = index;
            dragStartX = event.clientX;
            dragStartY = event.clientY;
            dragButton = button;

            button.style.setProperty('--drag-x', '0px');
            button.style.setProperty('--drag-y', '0px');
            button.setPointerCapture(event.pointerId);
            button.classList.add('dragging');
        });

        button.addEventListener('pointermove', event => {
            if (
                wordCompleted
                || dragIndex === null
                || dragButton !== button
            ) {
                return;
            }

            const dx = event.clientX - dragStartX;
            const dy = event.clientY - dragStartY;

            button.style.setProperty('--drag-x', `${dx}px`);
            button.style.setProperty('--drag-y', `${dy}px`);

            document.querySelectorAll('.unscramble-tile').forEach(tile => {
                if (tile !== button) {
                    tile.classList.remove('drag-over');
                }
            });

            const target = document
                .elementFromPoint(event.clientX, event.clientY)
                ?.closest('.unscramble-tile');

            if (target && target !== button) {
                target.classList.add('drag-over');
            }
        });

        button.addEventListener('pointerup', event => {
            if (
                wordCompleted
                || dragIndex === null
                || dragButton !== button
            ) {
                return;
            }

            const target = document
                .elementFromPoint(event.clientX, event.clientY)
                ?.closest('.unscramble-tile');

            button.classList.remove('dragging');
            button.style.removeProperty('--drag-x');
            button.style.removeProperty('--drag-y');

            document.querySelectorAll('.unscramble-tile').forEach(tile => {
                tile.classList.remove('drag-over');
            });

            if (target && target !== button) {
                const targetIndex = Array.from(
                    unscrambleRow.children
                ).indexOf(target);

                moveLetter(dragIndex, targetIndex);
            }

            dragIndex = null;
            dragButton = null;
        });

        unscrambleRow.appendChild(button);
    });
}

function moveLetter(fromIndex, toIndex) {
    if (
        wordCompleted
        || !Number.isInteger(fromIndex)
        || !Number.isInteger(toIndex)
        || fromIndex < 0
        || toIndex < 0
        || fromIndex >= currentLetters.length
        || toIndex >= currentLetters.length
        || fromIndex === toIndex
    ) {
        return;
    }

    const [moved] = currentLetters.splice(fromIndex, 1);
    currentLetters.splice(toIndex, 0, moved);

    hasMovedLetter = true;
    playLetter(moved.letter);
    clearFeedback();
    renderLetters();
    checkCompletion();
}

function currentAnswer() {
    return currentLetters.map(item => item.letter).join('');
}

function checkCompletion() {
    if (wordCompleted) {
        return;
    }

    const card = cards[currentIndex];

    if (currentAnswer() !== card.word) {
        return;
    }

    correctCount++;
    wordCompleted = true;
    renderLetters();

    setTimeout(() => {
        showFeedback('good', 'Great!');
        nextRow.classList.add('show');
    }, 350);

    if (wordAudioTimer) {
        clearTimeout(wordAudioTimer);
    }

    if (autoNextTimer) {
        clearTimeout(autoNextTimer);
    }

    wordAudioTimer = setTimeout(() => {
        playWord(card);
        wordAudioTimer = null;
    }, 1200);

    autoNextTimer = setTimeout(() => {
        autoNextTimer = null;
        goNext();
    }, 3000);
}

function renderCard() {
    if (wordAudioTimer) {
        clearTimeout(wordAudioTimer);
        wordAudioTimer = null;
    }
    if (autoNextTimer) {
        clearTimeout(autoNextTimer);
        autoNextTimer = null;
    }

    wordCompleted = false;
    const card = cards[currentIndex];

    dragIndex = null;
    hasMovedLetter = false;
    currentLetters = shuffle(
        [...card.word].map((letter, index) => ({
            id: `${index}-${letter}`,
            letter
        }))
    );

    // Avoid showing the correct order at the start whenever possible.
    if (
        card.word.length > 1
        && currentLetters.map(item => item.letter).join('') === card.word
    ) {
        [currentLetters[0], currentLetters[1]] =
            [currentLetters[1], currentLetters[0]];
    }

    if (card.image) {
        wordImage.src = card.image;
        wordImage.alt = card.display_word;
        wordImage.hidden = false;
        fallback.hidden = true;
    } else {
        wordImage.hidden = true;
        fallback.hidden = false;
    }

    progressLabel.textContent =
        `${currentIndex + 1} / ${cards.length}`;

    progressFill.style.width =
        `${((currentIndex + 1) / cards.length) * 100}%`;

    clearFeedback();
    nextRow.classList.remove('show');
    renderLetters();
}

function getScore() {
    if (cards.length === 0) {
        return 0;
    }

    const base = Math.round(
        (correctCount / cards.length) * 100
    );

    const penalty = Math.min(
        30,
        mistakes * 5
    );

    return Math.max(0, base - penalty);
}

function formatDuration(milliseconds) {
    const totalSeconds = Math.max(
        0,
        Math.round(milliseconds / 1000)
    );
    const minutes = String(
        Math.floor(totalSeconds / 60)
    ).padStart(2, '0');
    const seconds = String(
        totalSeconds % 60
    ).padStart(2, '0');

    return `${minutes}:${seconds}`;
}

async function saveProgress(score, elapsed) {
    if (isPreview || saving) {
        return null;
    }

    saving = true;

    try {
        const response = await fetch(
            '/EnglishSpeak/api/save-activity-progress.php',
            {
                method: 'POST',
                credentials: 'same-origin',
                cache: 'no-store',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                body: JSON.stringify({
                    activity_id: activityId,
                    score,
                    duration: elapsed,
                    completed: score >= Number(settings.passing_score || 80),
                    passing_score: Number(
                        settings.passing_score || 80
                    ),
                    preview: false
                })
            }
        );

        const result = await response.json().catch(() => null);

        if (!response.ok || !result || result.ok === false) {
            throw new Error(
                result?.message || 'Could not save progress.'
            );
        }

        return result.data || result;
    } catch (error) {
        console.warn(
            'Could not save Word Unscramble progress:',
            error
        );
        return null;
    } finally {
        saving = false;
    }
}

function showXpResult(result) {
    if (isPreview || !result || !xpPopup) {
        return;
    }

    const score = getScore();
    const passed = score >= Number(settings.passing_score || 80);

    /*
     * Chỉ dùng XP THỰC SỰ được cộng ở lượt hiện tại.
     * API trả về xp_added + xp_event_type.
     * Không dùng completion_xp/practice_xp tổng lịch sử để tránh hiển thị sai +10 ở lượt chơi lại.
     */
    const xpAdded = Number(result.xp_added || 0);
    const eventType = String(result.xp_event_type || '');

    xpPopupTitle.textContent = passed ? 'Great!' : 'Good effort!';

    if (!passed) {
        xpPopupText.textContent =
            'Em chưa đạt mức yêu cầu. Hãy thử lại nhé.';
        xpPopupScore.textContent = `${score}%`;
    } else if (xpAdded > 0) {
        if (eventType === 'completion') {
            xpPopupText.textContent = 'Completion Reward';
        } else {
            xpPopupText.textContent = 'Practice Reward';
        }

        xpPopupScore.textContent = `+${xpAdded} XP`;
    } else {
        xpPopupText.textContent = '';
        xpPopupScore.textContent = `${score}%`;
    }

    xpPopup.classList.add('show');
}

function hideXpResult() {
    xpPopup?.classList.remove('show');
}

async function finishGame() {
    const score = getScore();
    const elapsed = Date.now() - startedAt;
    const passed =
        score >= Number(settings.passing_score || 80);

    finalScore.textContent = `${score}/100`;
    finalTime.textContent = formatDuration(elapsed);

    if (continueButton) {
        continueButton.style.display = passed ? 'grid' : 'none';
    }

    if (score >= 90) {
        finishTitle.textContent = 'Great!';
        finishSubtitle.textContent = 'All done!';
    } else if (passed) {
        finishTitle.textContent = 'Good job!';
        finishSubtitle.textContent = 'Practice complete.';
    } else {
        finishTitle.textContent = 'Keep going!';
        finishSubtitle.textContent = 'Try again.';
    }

    gameScreen.style.display = 'none';
    finishScreen.classList.add('show');

    const progressResult = await saveProgress(score, elapsed);
    showXpResult(progressResult);
}

xpPopupButton?.addEventListener('click', hideXpResult);

$('imageButton')?.addEventListener('click', () => {
    playWord(cards[currentIndex]);
});

function goNext() {
    if (wordAudioTimer) {
        clearTimeout(wordAudioTimer);
        wordAudioTimer = null;
    }
    if (autoNextTimer) {
        clearTimeout(autoNextTimer);
        autoNextTimer = null;
    }

    if (currentIndex < cards.length - 1) {
        currentIndex++;
        renderCard();
        return;
    }

    finishGame();
}

$('nextButton')?.addEventListener('click', goNext);

$('againButton')?.addEventListener('click', () => {
    hideXpResult();
    currentIndex = 0;
    correctCount = 0;
    mistakes = 0;
    startedAt = Date.now();
    saving = false;

    finishScreen.classList.remove('show');
    gameScreen.style.display = 'block';
    renderCard();
});

renderCard();
</script>
<?php endif; ?>
</body>
</html>
