// weekly-quiz.jsx — Weekly Quiz page (wired to HSCWeeklyQuiz engine)
// Requires: wc-shared.jsx (Icon, TopBar, CountdownRing, TimerBar)
//           characters.js (window.HSCChars)
//           quiz-schedule.js (window.HSCQuizSchedule)
//           weekly-quiz.js (window.HSCWeeklyQuiz)

const SUBJECT_LABELS = {
  'biology':        'Biology',
  'chemistry':      'Chemistry',
  'physics':        'Physics',
  'maths-advanced': 'Maths Adv',
  'maths-standard': 'Maths Std',
};
const SUBJECTS = Object.keys(SUBJECT_LABELS);
const YEAR_GROUPS = ['y11', 'y12'];
const YEAR_LABELS = { y11: 'Year 11', y12: 'Year 12' };

const TOPIC_COLORS = {
  biology:          '#6b9b7c',
  chemistry:        '#6d5b8a',
  physics:          '#c47165',
  'maths-advanced': '#d6a85f',
  'maths-standard': '#7eb0d8',
};

const IS_DEV = window.location.search.includes('dev');

// weekKey is Saturday YYYY-MM-DD. Lock = that Friday +6 days at 23:59 AEST (+10:00).
function lockCountdown(weekKey) {
  var base = new Date(weekKey + 'T23:59:00+10:00');
  base.setDate(base.getDate() + 6);
  var msLeft = Math.max(0, base - new Date());
  return {
    days:  Math.floor(msLeft / 86400000),
    hours: Math.floor((msLeft % 86400000) / 3600000),
  };
}

function initials(name) {
  const parts = (name || 'U').replace(/_\d+$/, '').split(/[ _]+/);
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + (parts[parts.length - 1][0] || '')).toUpperCase();
}

function CharAvatar({ charId, size = 32, you }) {
  const chars = window.HSCChars;
  if (chars && charId) {
    const svgStr = chars.svg(charId, { size });
    if (svgStr) return (
      <span className="wq-char-wrap" style={{ width: size, height: size }}
        dangerouslySetInnerHTML={{ __html: svgStr }}/>
    );
  }
  return (
    <div className={'wc-row-avatar' + (you ? ' me' : '')}
      style={{ width: size, height: size, fontSize: Math.round(size * 0.36) }}>
      {initials(charId || '?')}
    </div>
  );
}

function CharRow({ row, compact }) {
  const rankCls = row.rank === 1 ? 'gold' : row.rank === 2 ? 'silver' : row.rank === 3 ? 'bronze' : '';
  return (
    <div className={'wc-row' + (row.isMe || row.you ? ' me' : '')}>
      <div className={'wc-rank ' + rankCls}>{row.rank}</div>
      <div className="wc-row-who">
        <CharAvatar charId={row.charId} size={30} you={row.isMe || row.you}/>
        <div>
          <div className="wc-row-name">
            {row.display || row.name}
            {(row.isMe || row.you) && <span className="wc-row-chip you-chip">YOU</span>}
          </div>
          {!compact && <div className="wc-row-tag">{row.tag || ''}</div>}
        </div>
      </div>
      <div className="wc-row-score">
        <strong>{Number(row.score).toLocaleString()}</strong>
        {!compact && <small>pts</small>}
      </div>
    </div>
  );
}

// ── Landing ──────────────────────────────────────────────────────────────────
function LandingState({ weekKey, subject, yearGroup, onSubjectChange, onYearChange, onStart, onStartGuest, isLoggedIn, authGate, leaderboard, loading, startError }) {
  const sched = window.HSCQuizSchedule;
  const banks = sched ? sched.getInScopeBanks(subject, yearGroup, weekKey) : [];

  return (
    <div className="wc-scope">
      <TopBar subtitle="Weekly Quiz"/>
      <div className="wc-shell">
        <section className="wc-hero">
          <div className="wc-hero-inner">
            <div>
              <div className="wc-hero-badges">
                <span className="wc-hero-badge"><Icon name="flag"/> {weekKey}</span>
                <span className="wc-hero-badge">Released Saturday</span>
                <span className="wc-hero-badge gold"><Icon name="bolt"/> +120 XP available</span>
              </div>
              <h1>Weekly <em>Quiz</em></h1>
              <p className="wc-hero-sub">
                15 curriculum-paced questions. Speed + streak multipliers. One attempt — results lock Friday 11:59 PM.
              </p>
              {authGate ? (
                <div className="wq-auth-gate">
                  <div className="wq-auth-gate-head">🔒 Sign in to appear on the leaderboard</div>
                  <div className="wq-auth-gate-body">
                    Your score won't be saved unless you're signed in. Sign in now to compete for medals — it only takes a second.
                  </div>
                  <div className="wq-auth-gate-btns">
                    <a href={'auth/login.html?return=' + encodeURIComponent('weekly-quiz.html')} className="wc-cta wc-cta-gold">
                      <Icon name="crown"/> Sign in to compete
                    </a>
                    <button className="wc-cta wc-cta-ghost" onClick={onStartGuest}>
                      Play as guest
                    </button>
                  </div>
                </div>
              ) : (
                <>
                  <div className="wc-hero-actions">
                    <button className="wc-cta wc-cta-gold" onClick={onStart} disabled={loading}>
                      <Icon name="play"/> {loading ? 'Loading…' : 'Start quiz'}
                    </button>
                    <button className="wc-cta wc-cta-ghost">How it works</button>
                  </div>
                  {isLoggedIn === false && (
                    <div className="wq-auth-hint">
                      🔒 Not signed in — <a href="auth/login.html?return=weekly-quiz.html">sign in</a> to appear on the leaderboard.
                    </div>
                  )}
                  {startError && <div className="wq-start-error">{startError}</div>}
                </>
              )}
            </div>
            <div className="wc-countdown">
              {(() => { const cd = weekKey ? lockCountdown(weekKey) : { days: 6, hours: 23 }; return <CountdownRing days={cd.days} hours={cd.hours}/>; })()}
              <div>
                <div className="wc-cd-label">Results lock</div>
                <div className="wc-cd-title">Friday 11:59 PM</div>
                <div className="wc-cd-sub">One attempt · no retakes · winner crowned Saturday</div>
              </div>
            </div>
          </div>
        </section>

        <div className="wq-filter-row">
          <div className="wq-filter-label">Subject</div>
          <div className="wq-pills">
            {SUBJECTS.map(s => (
              <button key={s} className={'wq-pill' + (subject === s ? ' active' : '')} onClick={() => onSubjectChange(s)}>
                {SUBJECT_LABELS[s]}
              </button>
            ))}
          </div>
          <div className="wq-toggle">
            {YEAR_GROUPS.map(y => (
              <button key={y} className={'wq-toggle-opt' + (yearGroup === y ? ' active' : '')} onClick={() => onYearChange(y)}>
                {YEAR_LABELS[y]}
              </button>
            ))}
          </div>
        </div>

        {banks.length === 0 ? (
          <div className="wc-link-row" style={{ color: 'var(--adv-muted)', marginTop: 14 }}>
            <Icon name="bulb"/> No question banks available yet for this subject and year. Try another combination.
          </div>
        ) : (
          <div className="wc-mix" style={{ margin: '14px 20px' }}>
            <div className="wc-mix-head">
              <div className="wc-mix-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ color: 'var(--adv-plum)', display: 'flex' }}><Icon name="book"/></span>
                {banks.length} module{banks.length > 1 ? 's' : ''} in this week's pool
              </div>
              <div className="wc-mix-sub">{SUBJECT_LABELS[subject]} · {YEAR_LABELS[yearGroup]}</div>
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 10 }}>
              {banks.map(b => {
                const parts = b.split('/');
                const yearNum = (parts[2] || '').replace('year', '');
                const modNum  = (parts[3] || '').replace('module', '');
                const label   = yearNum && modNum ? `Year ${yearNum} · Module ${modNum}` : parts.slice(1, 4).join(' / ');
                return (
                  <span key={b} style={{
                    display: 'inline-flex', alignItems: 'center', gap: 6,
                    padding: '5px 12px',
                    background: 'var(--adv-plum-soft)',
                    border: '1.5px solid var(--adv-plum)',
                    borderRadius: 99,
                    fontSize: 12.5, fontWeight: 700,
                    color: 'var(--adv-plum-deep)',
                    fontFamily: 'var(--adv-display)',
                  }}>
                    {label}
                  </span>
                );
              })}
            </div>
            <div style={{ marginTop: 10, fontSize: 12, color: 'var(--adv-muted)', fontWeight: 600 }}>
              25 questions per module · random selection each session
            </div>
          </div>
        )}

        {leaderboard.length > 0 && (
          <>
            <div className="wc-section-label">This week's board</div>
            <div className="wc-card wc-board" style={{ margin: '0 20px 40px' }}>
              <div className="wc-board-head">
                <div className="wc-board-icon rose"><Icon name="trophy"/></div>
                <div>
                  <div className="wc-board-title">{SUBJECT_LABELS[subject]} · {YEAR_LABELS[yearGroup]} · {weekKey}</div>
                  <div className="wc-board-sub">Live rankings · updated every 30s</div>
                </div>
              </div>
              {leaderboard.map(r => <CharRow key={r.rank + '-' + r.display} row={r}/>)}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ── Playing ───────────────────────────────────────────────────────────────────
function PlayingState({ questions, weekKey, subject, yearGroup, liveBoard, onComplete }) {
  const [qIdx, setQIdx] = React.useState(0);
  const [picked, setPicked] = React.useState(null);
  const [revealed, setRevealed] = React.useState(false);
  const [timeLeft, setTimeLeft] = React.useState(25);
  const streakRef = React.useRef(0);
  const startTimeRef = React.useRef(Date.now());
  const submittedRef = React.useRef(false);
  const timerRef = React.useRef(null);
  const answersRef = React.useRef([]);
  const qRef = React.useRef(null);

  const q = questions[qIdx];
  if (!q) return null;

  // Render LaTeX in question + options after each new question mounts
  React.useEffect(() => {
    if (!qRef.current || !window.renderMathInElement) return;
    window.renderMathInElement(qRef.current, {
      delimiters: [
        { left: '$$', right: '$$', display: true },
        { left: '$',  right: '$',  display: false },
      ],
      throwOnError: false,
    });
  }, [qIdx]);
  const runningScore = answersRef.current.reduce((s, a) => s + a.points, 0);

  React.useEffect(() => {
    submittedRef.current = false;
    startTimeRef.current = Date.now();
    setTimeLeft(25);
    setPicked(null);
    setRevealed(false);

    timerRef.current = setInterval(() => {
      setTimeLeft(prev => {
        if (prev <= 1) {
          clearInterval(timerRef.current);
          doSubmit(null);
          return 0;
        }
        return prev - 1;
      });
    }, 1000);

    return () => clearInterval(timerRef.current);
  }, [qIdx]);

  function doSubmit(letter) {
    if (submittedRef.current) return;
    submittedRef.current = true;
    clearInterval(timerRef.current);

    const timeMs = Math.min(25000, Date.now() - startTimeRef.current);
    const isCorrect = letter !== null && letter === ['A', 'B', 'C', 'D'][q.correctIndex];
    streakRef.current = isCorrect ? streakRef.current + 1 : 0;

    const score = window.HSCWeeklyQuiz.calcScore(timeMs, streakRef.current, isCorrect);

    const record = {
      questionId:    q.id,
      questionText:  q.prompt,
      picked:        letter,
      correct:       isCorrect,
      correctOption: q.options[q.correctIndex],
      timeMs,
      points:        score.points,
      speedMult:     score.speedMult,
      streakMult:    score.streakMult,
    };

    const next = answersRef.current.concat([record]);
    answersRef.current = next;
    setPicked(letter || '__timeout__');
    setRevealed(true);

    setTimeout(() => {
      if (next.length >= questions.length) {
        onComplete(next);
      } else {
        setQIdx(i => i + 1);
      }
    }, 2000);
  }

  function handlePick(letter) {
    if (submittedRef.current) return;
    doSubmit(letter);
  }

  const OPTS = ['A', 'B', 'C', 'D'];
  const isCorrectPick = picked && picked !== '__timeout__' && picked === OPTS[q.correctIndex];

  return (
    <div className="wc-scope">
      <TopBar subtitle={'Weekly Quiz · Q ' + (qIdx + 1) + '/15'}/>
      <div className="wq-quiz-grid">
        <div className="wq-main">
          <div className="wq-progress-wrap">
            <div className="wq-progress">
              <div className="wq-progress-fill" style={{ width: ((qIdx + 1) / 15 * 100) + '%' }}></div>
            </div>
            <span className="wq-progress-label">Q {qIdx + 1} of 15</span>
          </div>

          <div className="wc-qchrome">
            <div className="wc-q-counter">Question <strong>{qIdx + 1}</strong>/15</div>
            <div className="wc-q-topic">
              <div className="wc-q-topic-dot" style={{ background: TOPIC_COLORS[subject] }}></div>
              {SUBJECT_LABELS[subject]}
            </div>
            <div className="wc-q-streak">
              <div className="wc-q-streak-pill"><Icon name="flame"/> {streakRef.current} in a row</div>
              <div className="wc-q-mult">
                {streakRef.current >= 8 ? '×1.3' : streakRef.current >= 5 ? '×1.2' : streakRef.current >= 3 ? '×1.1' : '×1.0'}
              </div>
            </div>
          </div>

          <TimerBar pct={revealed ? 0 : timeLeft / 25} secs={String(timeLeft).padStart(2, '0')}/>

          <div ref={qRef}>
          <div className="wc-stem">
            {/* q.prompt is authored HTML from the curated question bank — not user input */}
            <div className="wc-stem-q" dangerouslySetInnerHTML={{ __html: q.prompt }}/>
          </div>

          <div className="wc-mc">
            {q.options.map((text, i) => {
              const letter = OPTS[i];
              const isPick = picked === letter;
              const cls = revealed
                ? (i === q.correctIndex ? 'correct' : isPick ? 'wrong' : 'dimmed')
                : (isPick ? 'correct' : '');
              return (
                <button key={letter} className={'wc-mc-opt ' + cls}
                  onClick={() => handlePick(letter)}
                  disabled={!!picked}>
                  <span className="wc-mc-badge">{letter}</span>
                  <span className="wc-mc-text">{text}</span>
                </button>
              );
            })}
          </div>
          </div>{/* /qRef */}

          {revealed && (
            <div className={'wc-fb ' + (isCorrectPick ? 'right' : 'wrong')}>
              <div className="wc-fb-icon">{isCorrectPick ? '✓' : '✕'}</div>
              <div>
                <div className="wc-fb-headline">
                  {isCorrectPick ? 'Correct!' : picked === '__timeout__' ? "Time's up." : 'Not quite.'}
                </div>
                <div className="wc-fb-explain">{q.explanation}</div>
              </div>
              {isCorrectPick && (
                <div className="wc-fb-side">
                  <div className="wc-fb-pts">+<em>{answersRef.current[answersRef.current.length - 1].points}</em></div>
                  <div className="wc-fb-pts-label">pts this question</div>
                </div>
              )}
            </div>
          )}

          <div className="wc-q-foot">
            <div className="wc-q-foot-meta">
              {runningScore > 0 && 'Score: ' + runningScore.toLocaleString() + ' pts · '}
              {!picked ? 'Select an answer' : revealed ? 'Advancing in 2s…' : ''}
            </div>
          </div>
        </div>

        <aside className="wq-side">
          <div className="wq-side-card">
            <div className="wq-side-head">
              <div className="wc-board-icon rose" style={{ width: 28, height: 28, borderRadius: 8 }}><Icon name="users"/></div>
              <div>
                <div className="wq-side-title">Live board</div>
                <div className="wq-side-sub">{SUBJECT_LABELS[subject]} · {YEAR_LABELS[yearGroup]}</div>
              </div>
            </div>
            {liveBoard.length === 0 ? (
              <div style={{ padding: '14px 16px', fontSize: 12, color: 'var(--adv-muted)' }}>No attempts yet — be the first!</div>
            ) : liveBoard.slice(0, 10).map(r => (
              <div key={r.rank + '-' + r.display} className={'wq-side-row' + (r.isMe ? ' wq-side-you' : '')}>
                <div className={'wc-rank' + (r.rank === 1 ? ' gold' : r.rank === 2 ? ' silver' : r.rank === 3 ? ' bronze' : '')}
                  style={{ width: 22, height: 22, fontSize: 10 }}>{r.rank}</div>
                <CharAvatar charId={r.charId} size={24} you={r.isMe}/>
                <div className="wq-side-name">{(r.display || '').split('#')[0]}{r.isMe ? ' (you)' : ''}</div>
                <div className="wq-side-score">{Number(r.score).toLocaleString()}</div>
              </div>
            ))}
            <div className="wq-side-updated">Completed attempts · polled 30s</div>
          </div>
        </aside>

        <div className="wq-mobile-bar">
          <div>Q {qIdx + 1}/15</div>
          <div><Icon name="flame"/> {streakRef.current}</div>
          <div>{runningScore.toLocaleString()} pts</div>
        </div>
      </div>
    </div>
  );
}

// ── Results body ──────────────────────────────────────────────────────────────
function ResultsBody({ answers = [], leaderboard = [], subject, yearGroup, weekKey }) {
  return (
    <>
      {leaderboard.length > 0 && (
        <>
          <div className="wc-section-label">Where you landed</div>
          <div className="wc-card wc-board" style={{ margin: '0 20px' }}>
            <div className="wc-board-head">
              <div className="wc-board-icon rose"><Icon name="trophy"/></div>
              <div>
                <div className="wc-board-title">{SUBJECT_LABELS[subject]} · {YEAR_LABELS[yearGroup]} · {weekKey}</div>
                <div className="wc-board-sub">{leaderboard.length} completed</div>
              </div>
            </div>
            {leaderboard.slice(0, 10).map(r => <CharRow key={r.rank + '-' + r.display} row={r}/>)}
          </div>
        </>
      )}
      {answers.length > 0 && (
        <div className="wc-breakdown" style={{ marginTop: 14 }}>
          <h3>Question breakdown</h3>
          <div className="wc-bd-rows">
            {answers.map((a, i) => (
              <div key={i} className="wc-bd-row">
                <div className="wc-bd-num">Q{i + 1}</div>
                <div className="wc-bd-q">{a.questionText}</div>
                <div className={'wc-bd-mark ' + (a.correct ? 'ok' : 'no')}><Icon name={a.correct ? 'check' : 'x'}/></div>
                <div className="wc-bd-pts">{a.correct ? '+' + a.points : '+0'}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </>
  );
}

// ── Results ───────────────────────────────────────────────────────────────────
function ResultsState({ attempt, leaderboard, subject, yearGroup, weekKey, isLoggedIn }) {
  const totalScore = attempt ? attempt.score : 0;
  const correct    = attempt ? attempt.correct : 0;
  const total      = attempt ? attempt.total : 15;
  const answers    = attempt ? attempt.answers : [];
  return (
    <div className="wc-scope">
      <TopBar subtitle="Weekly Quiz · Results"/>
      <div className="wc-shell">
        <section className="wc-results-hero">
          <div className="wc-results-inner">
            <div>
              <span className="wc-results-eye"><Icon name="trophy"/> {weekKey} · complete</span>
              <h1>Quiz <em>done!</em></h1>
              <p className="wc-results-headline">
                {correct} of {total} correct. Score locked — results on Friday 11:59 PM.
              </p>
              <div className="wc-results-cta-row">
                <a href="leaderboard.html" className="wc-cta wc-cta-gold"><Icon name="trophy"/> Open boards</a>
              </div>
            </div>
            <div className="wc-score-display">
              <div className="wc-score-big">{totalScore.toLocaleString()}<small> pts</small></div>
              <div className="wc-score-stats">
                <div className="wc-score-stat">
                  <div className="wc-score-stat-val">{total > 0 ? Math.round(correct / total * 100) : 0}%</div>
                  <div className="wc-score-stat-label">Accuracy</div>
                </div>
                <div className="wc-score-stat">
                  <div className="wc-score-stat-val">{correct}</div>
                  <div className="wc-score-stat-label">Correct</div>
                </div>
                <div className="wc-score-stat">
                  <div className="wc-score-stat-val">{total - correct}</div>
                  <div className="wc-score-stat-label">Wrong</div>
                </div>
              </div>
            </div>
          </div>
        </section>
        {isLoggedIn === false && (
          <div className="wq-guest-nudge">
            Score not saved — <a href={'auth/login.html?return=' + encodeURIComponent('weekly-quiz.html')}>sign in</a> next week to compete on the leaderboard.
          </div>
        )}
        <ResultsBody answers={answers} leaderboard={leaderboard} subject={subject} yearGroup={yearGroup} weekKey={weekKey}/>
      </div>
    </div>
  );
}

// ── Done ──────────────────────────────────────────────────────────────────────
function DoneState({ attempt, leaderboard, subject, yearGroup, weekKey }) {
  return (
    <div className="wc-scope">
      <TopBar subtitle="Weekly Quiz"/>
      <div className="wc-shell">
        <div className="wq-done-banner">
          <div>
            <div className="wq-done-eye"><Icon name="check"/> {weekKey} · already played</div>
            <div className="wq-done-title">Your score: <em>{attempt ? attempt.score.toLocaleString() : '—'} pts</em></div>
            <div className="wq-done-sub">Results lock Friday 11:59 PM. Winner crowned Saturday. Come back then for next week's quiz.</div>
          </div>
          <div className="wq-done-next">
            <div className="wq-done-next-label">Next quiz in</div>
            {(() => { const cd = weekKey ? lockCountdown(weekKey) : { days: 6, hours: 23 }; return <CountdownRing days={cd.days} hours={cd.hours}/>; })()}
          </div>
        </div>
        <ResultsBody answers={attempt ? attempt.answers : []} leaderboard={leaderboard} subject={subject} yearGroup={yearGroup} weekKey={weekKey}/>
      </div>
    </div>
  );
}

// ── Loading ───────────────────────────────────────────────────────────────────
function LoadingState({ message }) {
  return (
    <div className="wc-scope" style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}>
      <div style={{ textAlign: 'center', color: 'var(--adv-ink-soft)' }}>
        <div style={{ fontSize: 32, marginBottom: 12 }}>⏳</div>
        <div style={{ fontFamily: 'var(--adv-display)', fontWeight: 700 }}>{message || 'Loading…'}</div>
      </div>
    </div>
  );
}

// ── Dev toggle ────────────────────────────────────────────────────────────────
const DEV_PHASES = ['init', 'landing', 'loading_q', 'playing', 'saving', 'results', 'done'];
function DevToggle({ phase, setPhase }) {
  return (
    <div className="wq-dev">
      <span className="wq-dev-label">DEV</span>
      {DEV_PHASES.map(p => (
        <button key={p} className={'wq-dev-btn' + (phase === p ? ' active' : '')} onClick={() => setPhase(p)}>
          {p}
        </button>
      ))}
    </div>
  );
}

// ── App ───────────────────────────────────────────────────────────────────────
function WeeklyQuizApp() {
  const [phase, setPhase]         = React.useState('init');
  const [subject, setSubject]     = React.useState('biology');
  const [yearGroup, setYearGroup] = React.useState('y11');
  const [weekKey, setWeekKey]     = React.useState('');
  const [questions, setQuestions] = React.useState([]);
  const [attempt, setAttempt]     = React.useState(null);
  const [leaderboard, setLeaderboard] = React.useState([]);
  const [loadingQ, setLoadingQ]   = React.useState(false);
  const [startError, setStartError] = React.useState('');
  const [isLoggedIn, setIsLoggedIn] = React.useState(null);
  const [authGate, setAuthGate]   = React.useState(false);
  const pollRef = React.useRef(null);

  React.useEffect(() => {
    const authReady = (window.HSC && window.HSC.auth && window.HSC.auth.ready) || Promise.resolve();
    authReady.then(() => {
      const u = window.HSC && window.HSC.auth && window.HSC.auth.getUser && window.HSC.auth.getUser();
      setIsLoggedIn(!!u);
    }).catch(() => setIsLoggedIn(false));
  }, []);

  // Init: wait for auth, then check if already played this week
  React.useEffect(() => {
    const E = window.HSCWeeklyQuiz;
    if (!E) { setPhase('landing'); return; }
    const key = E.getCurrentWeekKey();
    setWeekKey(key);
    const authReady = (window.HSC && window.HSC.auth && window.HSC.auth.ready) || Promise.resolve();
    authReady.then(() => {
      setIsLoggedIn(!!(window.HSC && window.HSC.auth && window.HSC.auth.getUser && window.HSC.auth.getUser()));
      return E.hasPlayed(key, subject, yearGroup);
    }).then(saved => {
      if (saved) {
        setAttempt(saved);
        setPhase('done');
        refreshLeaderboard(key, subject, yearGroup);
      } else {
        setPhase('landing');
      }
    }).catch(() => setPhase('landing'));
  }, [subject, yearGroup]);

  // Poll leaderboard every 30s on landing/playing
  React.useEffect(() => {
    if (phase !== 'landing' && phase !== 'playing') {
      clearInterval(pollRef.current);
      return;
    }
    if (weekKey) refreshLeaderboard(weekKey, subject, yearGroup);
    pollRef.current = setInterval(() => {
      if (weekKey) refreshLeaderboard(weekKey, subject, yearGroup);
    }, 30000);
    return () => clearInterval(pollRef.current);
  }, [phase, weekKey, subject, yearGroup]);

  function refreshLeaderboard(key, sub, yr) {
    window.HSCWeeklyQuiz.getLeaderboard(key, sub, yr).then(setLeaderboard).catch(() => {});
  }

  function handleSubjectChange(s) {
    setSubject(s);
    setStartError('');
    setAuthGate(false);
    setPhase('init');
  }

  function handleYearGroupChange(y) {
    setYearGroup(y);
    setStartError('');
    setAuthGate(false);
    setPhase('init');
  }

  function handleStart() {
    if (isLoggedIn === false) { setAuthGate(true); return; }
    doStart();
  }

  function handleStartGuest() {
    setAuthGate(false);
    doStart();
  }

  function doStart() {
    const E = window.HSCWeeklyQuiz;
    const S = window.HSCQuizSchedule;
    if (!E || !S) return;
    setLoadingQ(true);
    const banks = S.getInScopeBanks(subject, yearGroup, weekKey);
    if (!banks.length) {
      setStartError('No questions available for this subject yet. Try another combination.');
      setLoadingQ(false);
      return;
    }
    E.loadBanks(banks).then(() => {
      const all = E.collectAllQuestions(banks);
      if (all.length < 5) {
        setStartError('Not enough questions in the bank yet — check back soon.');
        setLoadingQ(false);
        return;
      }
      const weekly   = E.selectWeekQuestions(all, weekKey, subject, yearGroup, 15);
      const authUser = window.HSC && window.HSC.auth && window.HSC.auth.getUser && window.HSC.auth.getUser();
      const userId   = authUser ? authUser.id : 'anon';
      const shuffled = weekly.map(q => E.shuffleAnswers(q, userId, weekKey));
      setQuestions(shuffled);
      setLoadingQ(false);
      setPhase('playing');
    }).catch(err => {
      console.error('[HSCWeeklyQuiz] Failed to load banks:', err);
      setStartError('Failed to load questions. Check your connection and try again.');
      setLoadingQ(false);
    });
  }

  function handleComplete(finalAnswers) {
    setPhase('saving');
    const totalScore = finalAnswers.reduce((s, a) => s + a.points, 0);
    const correct    = finalAnswers.filter(a => a.correct).length;
    const saved = { score: totalScore, correct, total: finalAnswers.length, answers: finalAnswers };
    window.HSCWeeklyQuiz.saveAttempt({
      weekKey,
      subject,
      yearGroup,
      score:   totalScore,
      correct,
      total:   finalAnswers.length,
      answers: finalAnswers,
    }).then(() => window.HSCWeeklyQuiz.getLeaderboard(weekKey, subject, yearGroup))
      .then(lb => {
        setLeaderboard(lb);
        setAttempt(saved);
        setPhase('results');
      })
      .catch(() => {
        setAttempt(saved);
        setPhase('results');
      });
  }

  const sharedProps = { subject, yearGroup, weekKey };

  return (
    <>
      {(phase === 'init' || phase === 'saving') && (
        <LoadingState message={phase === 'saving' ? 'Saving your result…' : 'Checking your progress…'}/>
      )}
      {phase === 'landing' && (
        <LandingState
          {...sharedProps}
          onSubjectChange={handleSubjectChange}
          onYearChange={handleYearGroupChange}
          onStart={handleStart}
          onStartGuest={handleStartGuest}
          isLoggedIn={isLoggedIn}
          authGate={authGate}
          leaderboard={leaderboard}
          loading={loadingQ}
          startError={startError}
        />
      )}
      {phase === 'playing' && (
        <PlayingState
          {...sharedProps}
          questions={questions}
          liveBoard={leaderboard}
          onComplete={handleComplete}
        />
      )}
      {phase === 'results' && (
        <ResultsState {...sharedProps} attempt={attempt} leaderboard={leaderboard} isLoggedIn={isLoggedIn}/>
      )}
      {phase === 'done' && (
        <DoneState {...sharedProps} attempt={attempt} leaderboard={leaderboard}/>
      )}
      {IS_DEV && <DevToggle phase={phase} setPhase={setPhase}/>}
    </>
  );
}
window.WeeklyQuizApp = WeeklyQuizApp;

