<?php
declare(strict_types=1);

session_start();

$isPreview = (int)($_GET['preview'] ?? 0) === 1;
$isTeacher = !empty($_SESSION['user_id'])
    && in_array((string)($_SESSION['user_role'] ?? ''), ['teacher', 'admin'], true);
$isStudent = !empty($_SESSION['student_id']);

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

require_once __DIR__ . '/../config/database.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 ssColumns(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 ssAssetUrl(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, './');
}

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

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

$activityTitleColumn = isset($activityColumns['title'])
    ? 'title'
    : (isset($activityColumns['name']) ? 'name' : null);

$setFk = isset($relationColumns['set_id'])
    ? 'set_id'
    : (isset($relationColumns['flashcard_set_id']) ? 'flashcard_set_id' : null);

$wordFk = isset($relationColumns['word_id']) ? 'word_id' : null;

$orderColumn = isset($relationColumns['order_no'])
    ? 'order_no'
    : (isset($relationColumns['sort_order']) ? 'sort_order' : 'id');

$wordTextColumn = isset($wordColumns['word'])
    ? 'word'
    : (isset($wordColumns['term']) ? 'term' : null);

$imageColumn = isset($wordColumns['image_path'])
    ? 'image_path'
    : (isset($wordColumns['image']) ? 'image' : null);

$audioColumn = isset($wordColumns['audio_us_path'])
    ? 'audio_us_path'
    : (isset($wordColumns['audio_path'])
        ? 'audio_path'
        : (isset($wordColumns['audio']) ? 'audio' : null));

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

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

$passingScoreSelect = isset($activityColumns['passing_score'])
    ? 'a.passing_score AS passing_score,'
    : '80 AS passing_score,';

$contextStmt = $pdo->prepare(
    "SELECT
        a.id AS activity_id,
        a.flashcard_set_id,
        a.xp_reward AS xp_reward,
        {$passingScoreSelect}
        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 Speaking trong cấp độ đã chọn.');
}

$passingScore = max(0, min(100, (int)($activity['passing_score'] ?? 80)));

if (ssColumns($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 !== '') {
        $decodedSettings = json_decode($settingsJson, true);

        if (is_array($decodedSettings)) {
            $passingScore = max(
                0,
                min(100, (int)($decodedSettings['passing_score'] ?? $passingScore))
            );
        }
    }
}

$xpReward = max(0, (int)($activity['xp_reward'] ?? 0));
if ($xpReward <= 0) {
    $xpReward = 15;
}

$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"
);
$wordStmt->execute([(int)$activity['flashcard_set_id']]);
$rows = $wordStmt->fetchAll(PDO::FETCH_ASSOC);

$cards = array_map(
    static function (array $row): array {
        return [
            'word' => (string)($row['word_text'] ?? ''),
            'image' => ssAssetUrl((string)($row['image_path'] ?? '')),
            'audio' => ssAssetUrl((string)($row['audio_path'] ?? '')),
        ];
    },
    $rows
);

$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,
    ]);

$_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>Speaking | EnglishSpeak</title>
<style>
:root{
  --blue:#198fe3;--blue-dark:#0868c7;--soft:#eaf5ff;--page:#f4f8fc;
  --text:#15314b;--muted:#718399;--line:#dfe8f0;--green:#26a65b;
  --orange:#f39c12;--red:#e55353;--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 12px 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 -12px 14px;padding:12px;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;cursor:pointer}
.heading{text-align:center;min-width:0}.heading strong{display:block;font-size:22px;line-height:1.15}.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 14px;overflow:hidden;border-radius:999px;background:#dceaf5}
.fill{width:0;height:100%;border-radius:inherit;background:linear-gradient(90deg,#1c7ce4,#18a9e7);transition:width .2s}
.speaking-card{min-height:calc(100dvh - 285px);padding:15px;border:1px solid var(--line);border-radius:22px;background:#fff;box-shadow:var(--shadow);display:flex;flex-direction:column;align-items:center}
.image-button{width:100%;height:min(34dvh,260px);min-height:180px;display:grid;place-items:center;padding:18px;border:0;border-radius:18px;background:#fbfdff;cursor:pointer}
.image-button img{max-width:250px;max-height:250px;width:88%;height:88%;object-fit:contain}
.fallback{font-size:96px;color:var(--blue)}
.sentence{margin:16px 0 0;color:var(--blue-dark);font-size:clamp(28px,8vw,40px);font-weight:850;line-height:1.2;text-align:center}
.actions{display:grid;grid-template-columns:1fr 1fr;gap:10px;width:100%;margin-top:auto;padding-top:22px}
.action{height:52px;padding:0 10px;border:1px solid #cbddeb;border-radius:15px;background:#fff;color:var(--text);font-weight:850;cursor:pointer}
.action.primary{border-color:var(--blue);background:linear-gradient(135deg,#237fe1,#159ddd);color:#fff}
.action.recording{background:var(--red);border-color:var(--red);color:#fff;animation:pulse 1.2s infinite}
@keyframes pulse{50%{box-shadow:0 0 0 8px rgba(229,83,83,.12)}}
.feedback{display:none;width:100%;margin-top:12px;padding:13px 14px;border-radius:15px;font-size:14px;line-height:1.45}
.feedback.show{display:block}.feedback.good{border:1px solid #bce5c8;background:#eefbf2;color:#19783a}.feedback.try{border:1px solid #f0d7a5;background:#fff8e8;color:#8a5c08}.feedback strong{display:block;margin-bottom:3px;font-size:16px}
.next-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;width:100%;margin-top:10px}
.next-row button{height:52px;padding:0 10px;border:1px solid #cbddeb;border-radius:15px;background:#fff;font-weight:850;cursor:pointer}
.next-row .next{border-color:var(--blue);background:var(--blue);color:#fff}
.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:66px}.finish h2{margin:14px 0 8px;color:#123e62;font-size:35px}.finish p{margin:0;color:var(--muted);font-size:16px}
.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-message{width:100%;margin-top:16px;padding:15px;border:1px solid #bde7c8;border-radius:16px;background:#eefbf1;color:#16823b;font-weight:750}
.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}
.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}.speaking-card{padding:12px}.image-button{height:min(31dvh,235px);min-height:170px}.image-button img{max-width:225px;max-height:225px}.sentence{font-size:clamp(25px,7.5vw,34px)}.action,.next-row button{font-size:13px}}

/* EnglishSpeak XP popup */
.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;
    animation: xpPopupIn .25s ease-out;
}
.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;
}
.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;
}
@keyframes xpPopupIn {
    from { opacity: 0; transform: translateY(14px) scale(.96); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}

</style>
</head>
<body>
<main class="app">
<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ó nội dung</h2><p>Hoạt động này chưa có dữ liệu.</p></section>
<?php else: ?>
<section id="practiceScreen">
  <div class="lesson"><div class="lesson__icon">🗣️</div><strong>Speaking</strong></div>
  <div class="progress-row"><span id="progressLabel">1 / <?= count($cards) ?></span><span id="scoreLabel">0%</span></div>
  <div class="track"><div class="fill" id="progressFill"></div></div>

  <section class="speaking-card">
    <button class="image-button" id="imageButton" type="button" aria-label="Nghe câu">
      <img id="wordImage" alt="" hidden>
      <span class="fallback" id="fallback">🖼️</span>
    </button>

    <div class="sentence" id="sentenceText"></div>

    <div class="actions">
      <button class="action" id="listenButton" type="button">🔊 Listen</button>
      <button class="action primary" id="speakButton" type="button">🎤 Giữ để nói</button>
    </div>

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

    <div class="next-row">
      <button id="retryButton" type="button">↻ Thử lại</button>
      <button class="next" id="nextButton" type="button">Tiếp →</button>
    </div>
  </section>
</section>

<section class="finish" id="finishScreen">
  <div class="finish__icon">🎉</div>
  <h2>Hoàn thành!</h2>
  <p>Bạn đã luyện xong <?= count($cards) ?> câu.</p>
  <div class="summary">
    <div class="summary-row"><span>Đã luyện</span><strong><?= count($cards) ?> / <?= count($cards) ?></strong></div>
    <div class="summary-row"><span>Điểm trung bình</span><strong id="finalScore">0%</strong></div>
  </div>
  <div class="finish-message">✅ Bạn đã hoàn thành phần luyện nói.</div>
  <div class="finish-actions">
    <button class="primary" id="replayButton" type="button">↻ Học lại</button>
    <a href="<?= htmlspecialchars($backUrl, ENT_QUOTES, 'UTF-8') ?>">☰ Quay lại hoạt động</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">Hoàn thành!</h2>
        <p class="xp-popup__text" id="xpPopupText">Bạn nhận được</p>
        <div class="xp-popup__score" id="xpPopupScore"></div>
        <button class="xp-popup__button" id="xpPopupButton" type="button">Xem kết quả</button>
    </div>
</div>

<?php if ($cards !== []): ?>
<script>

const isPreview = <?= $isPreview ? 'true' : 'false' ?>;
const passingScore = <?= (int)$passingScore ?>;
let savingProgress = false;

async function saveActivityProgress(score){
  if(isPreview || savingProgress){
    return isPreview
      ? {ok:true,preview:true,saved:false,xp_added:0,passed:score>=passingScore}
      : null;
  }

  savingProgress = true;

  try{
    const safeScore = Math.max(0, Math.min(100, Math.round(Number(score)||0)));
    const response = await fetch('/EnglishSpeak/api/save-activity-progress.php',{
      method:'POST',
      headers:{
        'Content-Type':'application/json;charset=UTF-8',
        'Accept':'application/json'
      },
      credentials:'same-origin',
      cache:'no-store',
      body:JSON.stringify({
        activity_id:<?= (int)$activityId ?>,
        score:safeScore,
        completed:safeScore>=passingScore,
        passing_score:passingScore,
        preview:false
      })
    });

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

    if(!response.ok || !result || result.ok!==true){
      console.warn('Không thể lưu tiến trình Speaking.', result);
      return null;
    }

    return result.data || result;
  }catch(error){
    console.warn('Lỗi mạng khi lưu tiến trình Speaking.', error);
    return null;
  }finally{
    savingProgress = false;
  }
}

function showXpPopup(result, activityScore){
  if(isPreview || !result || result.preview===true) return;

  const popup=document.getElementById('xpPopup');
  const title=document.getElementById('xpPopupTitle');
  const textEl=document.getElementById('xpPopupText');
  const scoreEl=document.getElementById('xpPopupScore');

  if(!popup || !title || !textEl || !scoreEl) return;

  const xpAdded=Number(result.xp_added||0);
  const safeScore=Math.max(0,Math.min(100,Math.round(Number(activityScore)||0)));
  const passed=
    result.passed===true
    || result.passed===1
    || result.passed==='1'
    || safeScore>=passingScore;

  if(xpAdded>0){
    title.textContent=result.xp_event_type==='completion' ? 'Hoàn thành!' : 'Luyện tập tốt!';
    textEl.hidden=false;
    textEl.textContent='Bạn nhận được';
    scoreEl.textContent=`+${xpAdded} XP`;
  }else if(passed){
    title.textContent='Great!';
    textEl.hidden=true;
    textEl.textContent='';
    scoreEl.textContent=`${safeScore}%`;
  }else{
    title.textContent='Good effort!';
    textEl.hidden=false;
    textEl.textContent='Bạn chưa đạt mức yêu cầu. Hãy thử lại nhé.';
    scoreEl.textContent=`${safeScore}%`;
  }

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

function hideXpPopup(){
  const popup=document.getElementById('xpPopup');
  if(popup) popup.classList.remove('show');
}
document.getElementById('xpPopupButton')?.addEventListener('click',hideXpPopup);


const baseCards = <?= json_encode($cards, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) ?>;
const cards = baseCards.map(card => ({
  ...card,
  sentence: card.example || `This is a ${card.word}.`
}));

let index=0;
let scores=[];
let recognition=null;
let isRecording=false;

const $=id=>document.getElementById(id);
const practiceScreen=$('practiceScreen');
const finishScreen=$('finishScreen');
const image=$('wordImage');
const fallback=$('fallback');
const sentence=$('sentenceText');
const progressLabel=$('progressLabel');
const progressFill=$('progressFill');
const scoreLabel=$('scoreLabel');
const feedback=$('feedback');
const speakButton=$('speakButton');

function normalize(s){
  return (s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'')
    .replace(/[^a-z0-9\s']/g,'').replace(/\s+/g,' ').trim();
}
function levenshtein(a,b){
  const m=a.length,n=b.length,dp=Array.from({length:m+1},()=>Array(n+1).fill(0));
  for(let i=0;i<=m;i++)dp[i][0]=i;
  for(let j=0;j<=n;j++)dp[0][j]=j;
  for(let i=1;i<=m;i++)for(let j=1;j<=n;j++){
    dp[i][j]=Math.min(
      dp[i-1][j]+1,
      dp[i][j-1]+1,
      dp[i-1][j-1]+(a[i-1]===b[j-1]?0:1)
    );
  }
  return dp[m][n];
}
function similarity(a,b){
  a=normalize(a);b=normalize(b);
  if(!a||!b)return 0;
  return Math.max(0,Math.round((1-levenshtein(a,b)/Math.max(a.length,b.length))*100));
}
function speak(text,audioUrl=''){
  if(audioUrl){
    const audio=new Audio(audioUrl);
    audio.play().catch(()=>tts(text));
  }else tts(text);
}
function tts(text){
  if(!('speechSynthesis' in window))return;
  speechSynthesis.cancel();
  const u=new SpeechSynthesisUtterance(text);
  u.lang='en-US';
  u.rate=.72;
  speechSynthesis.speak(u);
}
function render(){
  const c=cards[index];
  sentence.textContent=c.sentence;

  if(c.image){
    image.src=c.image;
    image.alt=c.word;
    image.hidden=false;
    fallback.hidden=true;
  }else{
    image.hidden=true;
    fallback.hidden=false;
  }

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

  const valid=scores.filter(Number.isFinite);
  const avg=valid.length?Math.round(valid.reduce((a,b)=>a+b,0)/valid.length):0;
  scoreLabel.textContent=`${avg}%`;

  feedback.className='feedback';
  feedback.innerHTML='';
}
function sentenceTip(target,heard){
  const targetWords=normalize(target).split(' ').filter(Boolean);
  const heardWords=normalize(heard).split(' ').filter(Boolean);

  for(const word of targetWords){
    if(!heardWords.includes(word)){
      const rules=[
        {re:/(ck|k|c|q)$/,sound:'/k/'},
        {re:/t$/,sound:'/t/'},
        {re:/d$/,sound:'/d/'},
        {re:/p$/,sound:'/p/'},
        {re:/b$/,sound:'/b/'},
        {re:/m$/,sound:'/m/'},
        {re:/n$/,sound:'/n/'},
        {re:/ng$/,sound:'/ŋ/'},
        {re:/l$/,sound:'/l/'},
        {re:/s$/,sound:'/s/'}
      ];
      const finalInfo=rules.find(r=>r.re.test(word));
      if(finalInfo){
        return `Từ “${word}” chưa nghe rõ, đặc biệt âm cuối ${finalInfo.sound}. Em đọc lại từ đó và bật âm cuối rõ hơn nhé.`;
      }
      return `Từ “${word}” chưa nghe rõ. Em đọc lại riêng từ này rồi nói lại cả câu nhé.`;
    }
  }

  return 'Một vài từ trong câu chưa rõ. Em nghe lại và đọc chậm từng từ nhé.';
}
function showFeedback(score,heard=''){
  const target=cards[index].sentence;
  const tip=sentenceTip(target,heard);

  if(score>=90){
    feedback.className='feedback show good';
    feedback.innerHTML=`<strong>🌟 Great! ${score}%</strong>Câu nói của em rõ và đủ từ.`;
  }else if(score>=75){
    feedback.className='feedback show good';
    feedback.innerHTML=`<strong>👍 Good! ${score}%</strong>${tip}`;
  }else{
    feedback.className='feedback show try';
    feedback.innerHTML=`<strong>🙂 Try again! ${score}%</strong>${tip}`;
  }

  scores[index]=Math.max(scores[index]||0,score);
  const valid=scores.filter(Number.isFinite);
  scoreLabel.textContent=`${valid.length?Math.round(valid.reduce((a,b)=>a+b,0)/valid.length):0}%`;
}
function startRecognition(){
  const SR=window.SpeechRecognition||window.webkitSpeechRecognition;

  if(!SR){
    feedback.className='feedback show try';
    feedback.innerHTML='<strong>Chưa hỗ trợ micro</strong>Trình duyệt này chưa hỗ trợ nhận diện giọng nói.';
    return;
  }

  recognition=new SR();
  recognition.lang='en-US';
  recognition.interimResults=false;
  recognition.continuous=false;

  recognition.onstart=()=>{
    isRecording=true;
    speakButton.classList.add('recording');
    speakButton.textContent='🔴 Đang nói...';
  };

  recognition.onresult=e=>{
    const heard=e.results[0][0].transcript;
    showFeedback(similarity(heard,cards[index].sentence),heard);
  };

  recognition.onerror=()=>{
    feedback.className='feedback show try';
    feedback.innerHTML='<strong>Thử lại nhé</strong>Không nghe rõ giọng nói của em.';
  };

  recognition.onend=()=>{
    isRecording=false;
    speakButton.classList.remove('recording');
    speakButton.textContent='🎤 Giữ để nói';
  };

  recognition.start();
}
function stopRecognition(){
  if(recognition&&isRecording){
    try{recognition.stop()}catch(e){}
  }
}
function next(){
  if(index<cards.length-1){
    index++;
    render();
    speak(cards[index].sentence);
  }else{
    const valid=scores.filter(Number.isFinite);
    const avg=valid.length?Math.round(valid.reduce((a,b)=>a+b,0)/valid.length):0;
    $('finalScore').textContent=`${avg}%`;

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

    saveActivityProgress(avg).then(result=>{
      showXpPopup(result,avg);
    });
  }
}

$('listenButton').onclick=()=>speak(cards[index].sentence);
$('imageButton').onclick=()=>speak(cards[index].sentence);
$('retryButton').onclick=()=>{
  feedback.className='feedback';
  speak(cards[index].sentence);
};
$('nextButton').onclick=next;

speakButton.addEventListener('pointerdown',e=>{
  e.preventDefault();
  startRecognition();
});
speakButton.addEventListener('pointerup',e=>{
  e.preventDefault();
  stopRecognition();
});
speakButton.addEventListener('pointerleave',stopRecognition);

$('replayButton').onclick=()=>{
  index=0;
  scores=[];
  finishScreen.classList.remove('show');
  hideXpPopup();
  savingProgress=false;
  practiceScreen.style.display='block';
  render();
};

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