// ============================================================
// DUEL — async 1v1 with ELO ladder
// State machine: hub → leaderboard → challenge_confirm → playing → waiting → results
// ============================================================

// ── Constants ────────────────────────────────────────────────

const SUBJECT_LABEL = {
  biology: 'Biology',
  chemistry: 'Chemistry',
  physics: 'Physics',
  'maths-standard': 'Maths Std',
};

const SUBJECT_PREFIX = {
  biology: 'bio',
  chemistry: 'chem',
  physics: 'phys',
  'maths-standard': 'maths-std',
};

// ── Pure helpers ──────────────────────────────────────────────

function loadScript(src) {
  if (document.querySelector(`script[src="${src}"]`)) return Promise.resolve();
  return new Promise((res, rej) => {
    const s = document.createElement('script');
    s.src = src; s.onload = res; s.onerror = rej;
    document.head.appendChild(s);
  });
}

async function ensureSchedule() {
  if (!window.HSCQuizSchedule) await loadScript('../../../../quiz-schedule.js');
}

// Pick 5 random question IDs ("lessonId::index") from current subject bank
async function pickQuestions(subject, yearGroup) {
  await ensureSchedule();
  const prefix = SUBJECT_PREFIX[subject];
  const yearKey = `y${yearGroup}`;
  const weekKey = window.HSCQuizSchedule.getCurrentWeekKey();
  const paths = window.HSCQuizSchedule.getInScopeBanks(subject, yearKey, weekKey);
  await Promise.all(paths.map(p => loadScript(`../../../../${p}`)));
  const all = [];
  Object.keys(window.HSCQuestionBankData).forEach(lid => {
    if (!lid.startsWith(`${prefix}-${yearKey}`)) return;
    window.HSCQuestionBankData[lid].questions.forEach((_, i) => all.push(`${lid}::${i}`));
  });
  for (let i = all.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [all[i], all[j]] = [all[j], all[i]];
  }
  return all.slice(0, 5);
}

// Reconstruct question objects from stored IDs
async function loadQuestionsFromIds(questionIds, subject, yearGroup) {
  await ensureSchedule();
  const yearKey = `y${yearGroup}`;
  const weekKey = window.HSCQuizSchedule.getCurrentWeekKey();
  const paths = window.HSCQuizSchedule.getInScopeBanks(subject, yearKey, weekKey);
  await Promise.all(paths.map(p => loadScript(`../../../../${p}`)));
  return questionIds.map(id => {
    const [lessonId, idx] = id.split('::');
    return window.HSCQuestionBankData[lessonId].questions[parseInt(idx)];
  });
}

// Standard Elo, K=32. result: 1=win 0.5=draw 0=loss
function calcElo(myElo, oppElo, result) {
  const exp = 1 / (1 + Math.pow(10, (oppElo - myElo) / 400));
  return Math.round(32 * (result - exp));
}

function hoursUntil(ts) {
  return Math.max(0, Math.round((new Date(ts) - Date.now()) / 3600000));
}

// ── App ───────────────────────────────────────────────────────

function DuelApp() {
  const db = window.HSC?.supabase;

  // Auth + profile
  const [user, setUser] = React.useState(null);
  const [profile, setProfile] = React.useState(null);

  // Navigation
  const [screen, setScreen] = React.useState('hub');
  const [subject, setSubject] = React.useState(null);

  // Hub
  const [myElo, setMyElo] = React.useState({ elo: 1200, wins: 0, losses: 0, rank: null });
  const [pending, setPending] = React.useState([]);
  const [duels, setDuels] = React.useState([]);
  const [hubLoading, setHubLoading] = React.useState(true);

  // Leaderboard
  const [leaderboard, setLeaderboard] = React.useState([]);
  const [lbLoading, setLbLoading] = React.useState(false);
  const [challengeTarget, setChallengeTarget] = React.useState(null);
  const [sendingChallenge, setSendingChallenge] = React.useState(false);

  // Playing
  const [duel, setDuel] = React.useState(null);
  const [questions, setQuestions] = React.useState([]);
  const [currentQ, setCurrentQ] = React.useState(0);
  const [selectedOption, setSelectedOption] = React.useState(null);
  const [showFeedback, setShowFeedback] = React.useState(false);
  const answersRef = React.useRef([]);
  const eloWrittenRef = React.useRef(false);
  const [waitingScore, setWaitingScore] = React.useState(0);

  // Results
  const [myRow, setMyRow] = React.useState(null);
  const [oppRow, setOppRow] = React.useState(null);
  const [eloResult, setEloResult] = React.useState(null);
  const [oppCodename, setOppCodename] = React.useState('Opponent');

  // Codename search
  const [searchVal, setSearchVal] = React.useState('');
  const [searchResult, setSearchResult] = React.useState(null);
  const [searching, setSearching] = React.useState(false);

  const [err, setErr] = React.useState(null);

  // ── Auth init ────────────────────────────────────────────────
  React.useEffect(() => {
    if (!db) { setErr('Sign in to play Duel'); setHubLoading(false); return; }
    db.auth.getSession().then(({ data: { session } }) => {
      if (session?.user) setUser(session.user);
      else { setErr('Sign in to play Duel'); setHubLoading(false); }
    });
    db.auth.onAuthStateChange((event, session) => {
      if (event === 'SIGNED_IN') setUser(session?.user || null);
      if (event === 'SIGNED_OUT') { setUser(null); setProfile(null); }
    });
  }, []);

  React.useEffect(() => {
    if (!user || !db) return;
    db.from('user_profiles')
      .select('codename, subjects, year')
      .eq('user_id', user.id)
      .maybeSingle()
      .then(({ data }) => {
        if (data) {
          setProfile(data);
          setSubject((data.subjects || [])[0] || 'biology');
        }
      });
  }, [user]);

  React.useEffect(() => {
    if (screen === 'hub' && user && subject && profile) loadHub();
  }, [screen, user, subject, profile]);

  // ── Hub data ─────────────────────────────────────────────────
  async function loadHub() {
    setHubLoading(true);
    try {
      // My ELO for this subject
      const { data: eloRow } = await db.from('duel_elo')
        .select('elo, wins, losses')
        .eq('user_id', user.id)
        .eq('subject', subject)
        .eq('year_group', profile.year)
        .maybeSingle();

      if (eloRow) {
        const { count } = await db.from('duel_elo')
          .select('*', { count: 'exact', head: true })
          .eq('subject', subject)
          .eq('year_group', profile.year)
          .gt('elo', eloRow.elo);
        setMyElo({ ...eloRow, rank: (count || 0) + 1 });
      } else {
        setMyElo({ elo: 1200, wins: 0, losses: 0, rank: null });
      }

      // Pending challenges where I'm the opponent
      const { data: pendingRows } = await db.from('duel_challenges')
        .select('id, challenger_id, subject, year_group, expires_at')
        .eq('opponent_id', user.id)
        .eq('status', 'pending')
        .order('created_at', { ascending: false });

      // Fetch challenger codenames
      const challengerIds = [...new Set((pendingRows || []).map(r => r.challenger_id))];
      let challengerMap = {};
      if (challengerIds.length) {
        const { data: cProfiles } = await db.from('user_profiles')
          .select('user_id, codename')
          .in('user_id', challengerIds);
        (cProfiles || []).forEach(p => { challengerMap[p.user_id] = p.codename; });
      }
      setPending((pendingRows || []).map(r => ({
        ...r,
        challengerCodename: challengerMap[r.challenger_id] || 'Unknown',
      })));

      // Active/complete duels involving me
      const { data: myDuels } = await db.from('duel_challenges')
        .select('id, challenger_id, opponent_id, subject, year_group, status, question_ids')
        .or(`challenger_id.eq.${user.id},opponent_id.eq.${user.id}`)
        .in('status', ['active', 'complete'])
        .order('created_at', { ascending: false });

      if (myDuels?.length) {
        const ids = myDuels.map(d => d.id);
        const { data: answered } = await db.from('duel_answers')
          .select('duel_id, user_id')
          .in('duel_id', ids);

        const byDuel = {};
        (answered || []).forEach(a => {
          if (!byDuel[a.duel_id]) byDuel[a.duel_id] = [];
          byDuel[a.duel_id].push(a.user_id);
        });

        const oppIds = [...new Set(myDuels.map(d =>
          d.challenger_id === user.id ? d.opponent_id : d.challenger_id
        ))];
        const { data: oppProfiles } = await db.from('user_profiles')
          .select('user_id, codename')
          .in('user_id', oppIds);
        const oppMap = {};
        (oppProfiles || []).forEach(p => { oppMap[p.user_id] = p.codename; });

        setDuels(myDuels.map(d => {
          const oppId = d.challenger_id === user.id ? d.opponent_id : d.challenger_id;
          const answerers = byDuel[d.id] || [];
          const iAnswered = answerers.includes(user.id);
          const bothAnswered = answerers.length >= 2;
          return {
            ...d,
            oppCodename: oppMap[oppId] || 'Unknown',
            oppId,
            duelState: bothAnswered ? 'ready' : (iAnswered ? 'waiting' : 'my_turn'),
          };
        }));
      } else {
        setDuels([]);
      }
    } catch (e) {
      setErr('Failed to load hub: ' + e.message);
    }
    setHubLoading(false);
  }

  // ── Accept / Decline pending challenge ───────────────────────
  async function acceptChallenge(challengeId) {
    const { error: e } = await db.from('duel_challenges').update({ status: 'active' }).eq('id', challengeId);
    if (e) { setErr('Failed to accept challenge: ' + e.message); return; }
    loadHub();
  }

  async function declineChallenge(challengeId) {
    const { error: e } = await db.from('duel_challenges').delete().eq('id', challengeId);
    if (e) { setErr('Failed to decline challenge: ' + e.message); return; }
    loadHub();
  }

  // ── Leaderboard ───────────────────────────────────────────────
  async function openLeaderboard() {
    setScreen('leaderboard');
    setLbLoading(true);
    try {
      const { data } = await db.from('duel_elo')
        .select('user_id, elo, wins, losses')
        .eq('subject', subject)
        .eq('year_group', profile.year)
        .order('elo', { ascending: false })
        .limit(50);

      const ids = (data || []).map(r => r.user_id);
      let codenameMap = {};
      if (ids.length) {
        const { data: profs } = await db.from('user_profiles')
          .select('user_id, codename')
          .in('user_id', ids);
        (profs || []).forEach(p => { codenameMap[p.user_id] = p.codename; });
      }
      setLeaderboard((data || []).map((row, i) => ({
        ...row,
        codename: codenameMap[row.user_id] || 'Unknown',
        rank: i + 1,
      })));
    } finally {
      setLbLoading(false);
    }
  }

  // ── Send challenge ────────────────────────────────────────────
  async function confirmChallenge() {
    if (!challengeTarget || !profile?.year) return;
    setSendingChallenge(true);
    try {
      const questionIds = await pickQuestions(subject, profile.year);
      if (questionIds.length < 5) {
        setErr(`Not enough questions available for ${SUBJECT_LABEL[subject] || subject} Year ${profile.year} yet.`);
        setSendingChallenge(false);
        return;
      }
      const expiresAt = new Date(Date.now() + 48 * 3600 * 1000).toISOString();
      const { error: e } = await db.from('duel_challenges').insert({
        challenger_id: user.id,
        opponent_id: challengeTarget.user_id,
        subject,
        year_group: profile.year,
        status: 'pending',
        question_ids: questionIds,
        expires_at: expiresAt,
      });
      if (e) { setErr('Failed to send challenge: ' + e.message); }
      else { setChallengeTarget(null); setScreen('hub'); }
    } catch (e) {
      setErr('Error: ' + e.message);
    }
    setSendingChallenge(false);
  }

  // ── Start playing ─────────────────────────────────────────────
  async function startPlaying(duelRow) {
    answersRef.current = [];
    eloWrittenRef.current = false;
    setWaitingScore(0);
    let qs;
    try {
      qs = await loadQuestionsFromIds(duelRow.question_ids, duelRow.subject, duelRow.year_group);
    } catch (e) {
      setErr('Failed to load questions: ' + e.message);
      return;
    }
    // Get opponent codename
    const oppId = duelRow.challenger_id === user.id ? duelRow.opponent_id : duelRow.challenger_id;
    const { data: oppProf } = await db.from('user_profiles')
      .select('codename').eq('user_id', oppId).maybeSingle();
    setOppCodename(oppProf?.codename || 'Opponent');
    setDuel(duelRow);
    setQuestions(qs);
    setCurrentQ(0);
    setSelectedOption(null);
    setShowFeedback(false);
    setScreen('playing');
  }

  // ── Answer selection ──────────────────────────────────────────
  function chooseOption(index) {
    if (showFeedback) return;
    const q = questions[currentQ];
    const correct = index === q.correctIndex;
    answersRef.current = [...answersRef.current, { q_id: currentQ, chosen: index, correct }];
    setSelectedOption(index);
    setShowFeedback(true);
  }

  async function nextQuestion() {
    if (currentQ < questions.length - 1) {
      setCurrentQ(c => c + 1);
      setSelectedOption(null);
      setShowFeedback(false);
      return;
    }
    // All 5 questions done
    const allAnswers = answersRef.current;
    const score = allAnswers.filter(a => a.correct).length;
    await submitAnswers(score, allAnswers);
  }

  // ── Submit answers ────────────────────────────────────────────
  async function submitAnswers(score, allAnswers) {
    const { error: e } = await db.from('duel_answers').insert({
      duel_id: duel.id,
      user_id: user.id,
      answers: allAnswers,
      score,
      completed_at: new Date().toISOString(),
    });
    if (e) { setErr('Failed to save answers: ' + e.message); return; }

    // Check if opponent also answered
    const { data: allAnswerRows } = await db.from('duel_answers')
      .select('user_id, score, answers, completed_at')
      .eq('duel_id', duel.id);

    const myAnswerRow = allAnswerRows?.find(a => a.user_id === user.id);
    const oppAnswerRow = allAnswerRows?.find(a => a.user_id !== user.id);

    if (oppAnswerRow) {
      // Both done — update status and show results
      await db.from('duel_challenges').update({ status: 'complete' }).eq('id', duel.id);
      setMyRow(myAnswerRow || { score, answers: allAnswers });
      setOppRow(oppAnswerRow);
      await computeAndWriteElo(score, oppAnswerRow.score);
      setScreen('results');
    } else {
      setWaitingScore(score);
      setScreen('waiting');
    }
  }

  // ── Check waiting duel (on revisit) ──────────────────────────
  async function checkWaiting() {
    if (!duel) return;
    const { data: rows, error: e } = await db.from('duel_answers')
      .select('user_id, score, answers, completed_at')
      .eq('duel_id', duel.id);
    if (e) { setErr('Failed to check duel status: ' + e.message); return; }
    const mine = rows?.find(a => a.user_id === user.id);
    const theirs = rows?.find(a => a.user_id !== user.id);
    if (mine && theirs) {
      setMyRow(mine);
      setOppRow(theirs);
      await computeAndWriteElo(mine.score, theirs.score); // eloWrittenRef prevents double-write
      setScreen('results');
    }
  }

  // ── Open a ready duel to results ─────────────────────────────
  async function openReadyDuel(duelRow) {
    answersRef.current = [];
    const oppId = duelRow.challenger_id === user.id ? duelRow.opponent_id : duelRow.challenger_id;
    const { data: oppProf } = await db.from('user_profiles')
      .select('codename').eq('user_id', oppId).maybeSingle();
    setOppCodename(oppProf?.codename || 'Opponent');
    setDuel(duelRow);

    const { data: rows, error: e } = await db.from('duel_answers')
      .select('user_id, score, answers')
      .eq('duel_id', duelRow.id);
    if (e) { setErr('Failed to load results: ' + e.message); return; }
    const mine = rows?.find(a => a.user_id === user.id);
    const theirs = rows?.find(a => a.user_id !== user.id);
    setMyRow(mine);
    setOppRow(theirs);
    // ELO already written — just show results
    setEloResult(null);
    setScreen('results');
  }

  // ── Compute + write ELO ───────────────────────────────────────
  async function computeAndWriteElo(myScore, oppScore) {
    if (eloWrittenRef.current) return; // prevent double-write if checkWaiting called twice
    eloWrittenRef.current = true;
    const oppId = duel.challenger_id === user.id ? duel.opponent_id : duel.challenger_id;

    const [{ data: myEloRow }, { data: oppEloRow }] = await Promise.all([
      db.from('duel_elo').select('elo, wins, losses')
        .eq('user_id', user.id).eq('subject', duel.subject).eq('year_group', duel.year_group).maybeSingle(),
      db.from('duel_elo').select('elo')
        .eq('user_id', oppId).eq('subject', duel.subject).eq('year_group', duel.year_group).maybeSingle(),
    ]);

    const curMyElo = myEloRow?.elo ?? 1200;
    const curOppElo = oppEloRow?.elo ?? 1200;
    const result = myScore > oppScore ? 1 : (myScore === oppScore ? 0.5 : 0);
    const delta = calcElo(curMyElo, curOppElo, result);
    const newElo = curMyElo + delta;

    await db.from('duel_elo').upsert({
      user_id: user.id,
      subject: duel.subject,
      year_group: duel.year_group,
      elo: newElo,
      wins: (myEloRow?.wins ?? 0) + (result === 1 ? 1 : 0),
      losses: (myEloRow?.losses ?? 0) + (result === 0 ? 1 : 0),
      updated_at: new Date().toISOString(),
    }, { onConflict: 'user_id,subject,year_group' });

    setEloResult({ delta, newElo, result });
  }

  // ── Codename search ───────────────────────────────────────────
  async function searchCodename() {
    if (!searchVal.trim()) return;
    setSearching(true);
    setSearchResult(null);
    const { data } = await db.from('user_profiles')
      .select('user_id, codename, year')
      .ilike('codename', searchVal.trim())
      .limit(1)
      .maybeSingle();
    setSearchResult(data || null);
    setSearching(false);
  }

  // ── Render ────────────────────────────────────────────────────

  if (err && !user) {
    return (
      <SdScope theme="mixed" style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <div className="du-msg">{err} <a href="../../../../auth/login.html">Sign in →</a></div>
      </SdScope>
    );
  }

  return (
    <SdScope theme="mixed" style={{ minHeight: '100vh', background: '#f0eee9' }}>
      {screen === 'hub'               && renderHub()}
      {screen === 'leaderboard'       && renderLeaderboard()}
      {screen === 'challenge_confirm' && renderChallengeConfirm()}
      {screen === 'playing'           && renderPlaying()}
      {screen === 'waiting'           && renderWaiting()}
      {screen === 'results'           && renderResults()}
    </SdScope>
  );

  // ── Hub screen ────────────────────────────────────────────────
  function renderHub() {
    const subjects = profile?.subjects || ['biology'];
    return (
      <div className="du-page">
        <div className="du-hub-head">
          <div className="du-hub-title">⚔ DUEL <small>· async 1v1</small></div>
          {/* Subject tabs */}
          <div className="du-subject-tabs">
            {subjects.map(s => (
              <button
                key={s}
                type="button"
                className={`du-subject-tab${s === subject ? ' active' : ''}`}
                onClick={() => setSubject(s)}
              >
                {SUBJECT_LABEL[s] || s}
              </button>
            ))}
          </div>
        </div>

        <div className="du-hub-body">
          {/* ELO card */}
          <div className="du-elo-card">
            <div className="du-elo-num">{myElo.elo}</div>
            <div className="du-elo-label">ELO {myElo.rank ? `· Rank #${myElo.rank}` : '· Unranked'}</div>
            <div className="du-elo-record">{myElo.wins}W {myElo.losses}L</div>
          </div>

          {/* Find opponent + search */}
          <div className="du-actions">
            <button type="button" className="du-btn-primary" onClick={openLeaderboard}>Find opponent</button>
            <div className="du-search-row">
              <input
                className="du-search-input"
                placeholder="Search by codename…"
                value={searchVal}
                onChange={e => { setSearchVal(e.target.value); setSearchResult(null); }}
                onKeyDown={e => e.key === 'Enter' && searchCodename()}
              />
              <button type="button" className="du-btn-ghost" onClick={searchCodename} disabled={searching}>
                {searching ? '…' : 'Find'}
              </button>
            </div>
            {searchResult && (
              <div className="du-search-result">
                <span className="du-search-codename">{searchResult.codename}</span>
                <button
                  type="button"
                  className="du-btn-small"
                  onClick={() => { setChallengeTarget(searchResult); setScreen('challenge_confirm'); }}
                >
                  Challenge
                </button>
              </div>
            )}
            {searchResult === null && !searching && searchVal && (
              <div className="du-search-empty">No student found with that codename.</div>
            )}
          </div>

          {/* Pending challenges inbox */}
          {pending.length > 0 && (
            <div className="du-section">
              <div className="du-section-label">Challenges waiting for you</div>
              {pending.map(c => (
                <div key={c.id} className="du-inbox-row">
                  <div className="du-inbox-info">
                    <strong>{c.challengerCodename}</strong> challenged you
                    <span className="du-inbox-subject"> · {SUBJECT_LABEL[c.subject] || c.subject}</span>
                    <span className="du-inbox-expires"> · {hoursUntil(c.expires_at)}h left</span>
                  </div>
                  <div className="du-inbox-btns">
                    <button type="button" className="du-btn-small primary" onClick={() => acceptChallenge(c.id)}>Accept</button>
                    <button type="button" className="du-btn-small" onClick={() => declineChallenge(c.id)}>Decline</button>
                  </div>
                </div>
              ))}
            </div>
          )}

          {/* Active duels */}
          {duels.length > 0 && (
            <div className="du-section">
              <div className="du-section-label">Your duels</div>
              {duels.map(d => (
                <div key={d.id} className={`du-duel-row du-duel-${d.duelState}`}>
                  <div className="du-duel-info">
                    <strong>{d.oppCodename}</strong>
                    <span className="du-duel-subject"> · {SUBJECT_LABEL[d.subject] || d.subject}</span>
                  </div>
                  <div className="du-duel-state-badge">
                    {d.duelState === 'my_turn'  && 'Your turn'}
                    {d.duelState === 'waiting'  && 'Awaiting opponent'}
                    {d.duelState === 'ready'    && 'Results ready'}
                  </div>
                  {d.duelState === 'my_turn' && (
                    <button type="button" className="du-btn-small primary" onClick={() => startPlaying(d)}>Play</button>
                  )}
                  {d.duelState === 'waiting' && (
                    <button type="button" className="du-btn-small" onClick={async () => {
                      setDuel(d);
                      const { data: myAns } = await db.from('duel_answers')
                        .select('score').eq('duel_id', d.id).eq('user_id', user.id).maybeSingle();
                      setWaitingScore(myAns?.score ?? 0);
                      setOppCodename(d.oppCodename || 'Opponent');
                      setScreen('waiting');
                    }}>View</button>
                  )}
                  {d.duelState === 'ready' && (
                    <button type="button" className="du-btn-small primary" onClick={() => openReadyDuel(d)}>Results</button>
                  )}
                </div>
              ))}
            </div>
          )}

          {hubLoading && <div className="du-loading">Loading…</div>}
          {!hubLoading && duels.length === 0 && pending.length === 0 && (
            <div className="du-empty">No active duels. Find an opponent to start one.</div>
          )}
        </div>
      </div>
    );
  }

  // ── Leaderboard screen ────────────────────────────────────────
  function renderLeaderboard() {
    return (
      <div className="du-page">
        <div className="du-nav-head">
          <button type="button" className="du-back-btn" onClick={() => setScreen('hub')}>← Hub</button>
          <div className="du-nav-title">{SUBJECT_LABEL[subject] || subject} Ladder</div>
        </div>
        <div className="du-hub-body">
          {lbLoading && <div className="du-loading">Loading ladder…</div>}
          {!lbLoading && leaderboard.length === 0 && (
            <div className="du-empty">No duel records yet. Be the first!</div>
          )}
          {leaderboard.map(row => (
            <div
              key={row.user_id}
              className={`du-lb-row${row.user_id === user?.id ? ' du-lb-me' : ''}`}
              onClick={() => {
                if (row.user_id !== user?.id) {
                  setChallengeTarget(row);
                  setScreen('challenge_confirm');
                }
              }}
            >
              <div className="du-lb-rank">#{row.rank}</div>
              <div className="du-lb-name">{row.codename}</div>
              <div className="du-lb-elo">{row.elo} ELO</div>
              <div className="du-lb-record">{row.wins}W {row.losses}L</div>
              {row.user_id !== user?.id && <div className="du-lb-challenge-hint">Tap to challenge</div>}
            </div>
          ))}
        </div>
      </div>
    );
  }

  // ── Challenge confirm screen ──────────────────────────────────
  function renderChallengeConfirm() {
    if (!challengeTarget) return null;
    return (
      <div className="du-page du-page-center">
        <div className="du-confirm-card">
          <div className="du-confirm-title">Challenge</div>
          <div className="du-confirm-name">{challengeTarget.codename}</div>
          <div className="du-confirm-detail">Best of 5 · {SUBJECT_LABEL[subject] || subject} · 48h to respond</div>
          <div className="du-confirm-elo">Their ELO: {challengeTarget.elo || 1200}</div>
          <div className="du-confirm-btns">
            <button
              type="button"
              className="du-btn-primary"
              onClick={confirmChallenge}
              disabled={sendingChallenge}
            >
              {sendingChallenge ? 'Sending…' : 'Send challenge'}
            </button>
            <button type="button" className="du-btn-ghost" onClick={() => {
              setChallengeTarget(null);
              setScreen(leaderboard.length ? 'leaderboard' : 'hub');
            }}>
              Cancel
            </button>
          </div>
        </div>
      </div>
    );
  }

  // ── Playing screen ────────────────────────────────────────────
  function renderPlaying() {
    if (!questions.length) return <div className="du-loading">Loading questions…</div>;
    const q = questions[currentQ];
    const isLast = currentQ === questions.length - 1;
    return (
      <div className="du-page">
        <div className="du-play-head">
          <button type="button" className="du-back-btn" onClick={() => setScreen('hub')}>← Hub</button>
          <div className="du-play-opp">vs {oppCodename}</div>
        </div>
        <div className="du-progress-bar">
          <div className="du-progress-fill" style={{ width: `${((currentQ) / questions.length) * 100}%` }}></div>
        </div>
        <div className="du-play-qnum">Q{currentQ + 1} of {questions.length}</div>

        <div className="du-hub-body">
          <div className="du-question-text">{q.prompt}</div>
          <div className="du-options">
            {q.options.map((opt, i) => {
              let cls = 'du-option';
              if (showFeedback) {
                if (i === q.correctIndex) cls += ' correct';
                else if (i === selectedOption) cls += ' wrong';
              }
              return (
                <button key={i} type="button" className={cls} onClick={() => chooseOption(i)}>
                  <span className="du-option-letter">{['A','B','C','D'][i]}</span>
                  <span className="du-option-text">{opt}</span>
                </button>
              );
            })}
          </div>
          {showFeedback && (
            <div className="du-feedback">
              {selectedOption === q.correctIndex
                ? <span className="du-feedback-correct">Correct!</span>
                : <span className="du-feedback-wrong">Incorrect — answer was {['A','B','C','D'][q.correctIndex]}</span>
              }
              <button type="button" className="du-btn-primary du-next-btn" onClick={nextQuestion}>
                {isLast ? 'Finish' : 'Next →'}
              </button>
            </div>
          )}
        </div>
      </div>
    );
  }

  // ── Waiting screen ────────────────────────────────────────────
  function renderWaiting() {
    return (
      <div className="du-page du-page-center">
        <div className="du-waiting-card">
          <div className="du-waiting-icon">⏳</div>
          <div className="du-waiting-title">Waiting for {oppCodename}</div>
          <div className="du-waiting-score">You scored {waitingScore}/5</div>
          <div className="du-waiting-sub">Come back when they've answered to see results.</div>
          <div className="du-confirm-btns">
            <button type="button" className="du-btn-primary" onClick={checkWaiting}>Check now</button>
            <button type="button" className="du-btn-ghost" onClick={() => setScreen('hub')}>Back to hub</button>
          </div>
        </div>
      </div>
    );
  }

  // ── Results screen ────────────────────────────────────────────
  function renderResults() {
    if (!myRow || !oppRow) return <div className="du-loading">Loading results…</div>;
    const myScore = myRow.score;
    const oppScore = oppRow.score;
    const verdict = myScore > oppScore ? 'win' : (myScore === oppScore ? 'draw' : 'loss');
    const verdictLabel = { win: 'You won!', draw: 'Draw!', loss: 'You lost.' }[verdict];

    return (
      <div className="du-page">
        <div className="du-nav-head">
          <button type="button" className="du-back-btn" onClick={() => setScreen('hub')}>← Hub</button>
          <div className="du-nav-title">Results</div>
        </div>
        <div className="du-hub-body">
          {/* Score card */}
          <div className={`du-result-card du-result-${verdict}`}>
            <div className="du-result-verdict">{verdictLabel}</div>
            <div className="du-result-scores">
              <div className="du-result-score-block">
                <div className="du-result-score-num">{myScore}</div>
                <div className="du-result-score-label">You</div>
              </div>
              <div className="du-result-vs">vs</div>
              <div className="du-result-score-block">
                <div className="du-result-score-num">{oppScore}</div>
                <div className="du-result-score-label">{oppCodename}</div>
              </div>
            </div>
            {eloResult && (
              <div className={`du-elo-delta ${eloResult.delta >= 0 ? 'positive' : 'negative'}`}>
                {eloResult.delta >= 0 ? '+' : ''}{eloResult.delta} ELO → {eloResult.newElo}
              </div>
            )}
          </div>

          {/* Question breakdown */}
          {Array.isArray(myRow.answers) && (
            <div className="du-section">
              <div className="du-section-label">Question breakdown</div>
              {(myRow.answers || []).map((a, i) => (
                <div key={i} className={`du-breakdown-row ${a.correct ? 'correct' : 'wrong'}`}>
                  <span className="du-breakdown-qnum">Q{i + 1}</span>
                  <span className="du-breakdown-result">{a.correct ? '✓' : '✗'}</span>
                  {oppRow.answers?.[i] !== undefined && (
                    <span className="du-breakdown-opp">
                      {oppCodename}: {oppRow.answers[i]?.correct ? '✓' : '✗'}
                    </span>
                  )}
                </div>
              ))}
            </div>
          )}

          {/* Actions */}
          <div className="du-confirm-btns">
            <button type="button" className="du-btn-primary" onClick={() => {
              if (duel) {
                const oppId = duel.challenger_id === user?.id ? duel.opponent_id : duel.challenger_id;
                setChallengeTarget({ user_id: oppId, codename: oppCodename, elo: oppRow ? (oppRow.elo || 1200) : 1200 });
                setScreen('challenge_confirm');
              }
            }}>Challenge again</button>
            <button type="button" className="du-btn-ghost" onClick={() => setScreen('hub')}>Back to hub</button>
          </div>
        </div>
      </div>
    );
  }
}

ReactDOM.createRoot(document.getElementById('root')).render(<DuelApp/>);
