/* The Critical Lens — React app */

const { useState, useEffect, useMemo, useCallback, useRef } = React;
const DATA = window.LENS_DATA;
const ORDER = DATA.order;
const LENSES = ORDER.map((id) => DATA.lenses[id]);
const BY_ID = DATA.lenses;
const QUESTIONS_PER_QUIZ = 20;

// ─── Firebase (leaderboard + stats) ───────────────────────────
const FB_CFG = window.FIREBASE_CONFIG || {};
const FB_READY =
  !!window.firebase && !!FB_CFG.apiKey && FB_CFG.apiKey !== "PASTE_API_KEY";

let _db = null;
function db() {
  if (!FB_READY) return null;
  if (_db) return _db;
  if (!firebase.apps.length) firebase.initializeApp(FB_CFG);
  _db = firebase.firestore();
  return _db;
}
const SCORES = () => db().collection("lens_scores");
const LSTATS = () => db().collection("lens_stats").doc("global");

async function fetchLeaderboard() {
  if (!FB_READY) return null;
  try {
    const snap = await SCORES().orderBy("pct", "desc").limit(50).get();
    const rows = snap.docs.map((d) => ({ id: d.id, ...d.data() }));
    // stable tie-break: higher pct first, then earlier timestamp
    rows.sort((a, b) => (b.pct - a.pct) || ((a.ts?.seconds || 0) - (b.ts?.seconds || 0)));
    return rows;
  } catch (e) {
    console.warn("leaderboard read failed", e);
    return null;
  }
}

async function fetchLensStats() {
  if (!FB_READY) return null;
  try {
    const snap = await LSTATS().get();
    return snap.exists ? snap.data() : { totalAttempts: 0, scoreSumPct: 0, misidentified: {} };
  } catch (e) {
    console.warn("stats read failed", e);
    return null;
  }
}

// Writes the leaderboard row, then rolls the aggregate stats in a transaction so
// values satisfy the Firestore rules (totalAttempts only ever rises by 1).
async function submitScore({ name, pct, correct, total, mode, wrongLensIds }) {
  if (!FB_READY) return null;
  try {
    const ref = await SCORES().add({
      name, pct, correct, total, mode,
      ts: firebase.firestore.FieldValue.serverTimestamp(),
    });
    const sref = LSTATS();
    await db().runTransaction(async (t) => {
      const snap = await t.get(sref);
      const cur = snap.exists ? snap.data() : { totalAttempts: 0, scoreSumPct: 0, misidentified: {} };
      const mis = Object.assign({}, cur.misidentified || {});
      wrongLensIds.forEach((id) => { mis[id] = (mis[id] || 0) + 1; });
      const next = {
        totalAttempts: (cur.totalAttempts || 0) + 1,
        scoreSumPct: (cur.scoreSumPct || 0) + pct,
        misidentified: mis,
      };
      if (snap.exists) t.update(sref, next);
      else t.set(sref, next);
    });
    return ref.id;
  } catch (e) {
    console.warn("score write failed", e);
    return null;
  }
}

// ─── helpers ──────────────────────────────────────────────────
function shuffle(arr) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}
function pad(n) { return String(n).padStart(2, "0"); }
function fmtDate(ts) {
  if (!ts || !ts.seconds) return "—";
  return new Date(ts.seconds * 1000).toLocaleDateString("en-CA", { month: "short", day: "numeric" });
}

const BAD_WORDS = ["ass", "fuk", "fuc", "fck", "sex", "cum", "tit", "fag", "nig", "cnt", "dic", "pns", "gay", "hoe", "sht", "wtf", "jew", "kkk"];
function cleanInitials(raw) {
  return (raw || "").toUpperCase().replace(/[^A-Z]/g, "").slice(0, 4);
}
function isBad(initials) {
  const s = initials.toLowerCase();
  return BAD_WORDS.some((w) => s.includes(w));
}

// Flatten every prompt for a mode into {lensId, text}, then build a question
// sequence of length n with no two consecutive prompts from the same lens.
function buildQuiz(mode) {
  const prompts = [];
  LENSES.forEach((l) => l.quiz[mode].forEach((text) => prompts.push({ lensId: l.id, text })));
  const n = QUESTIONS_PER_QUIZ;
  const seq = [];
  let bag = [];
  while (seq.length < n) {
    if (!bag.length) {
      bag = shuffle(prompts);
      if (seq.length && bag.length > 1 && bag[0].lensId === seq[seq.length - 1].lensId) {
        [bag[0], bag[1]] = [bag[1], bag[0]];
      }
    }
    const p = bag.shift();
    // avoid an identical prompt appearing twice in a row after a reshuffle
    if (seq.length && p.text === seq[seq.length - 1].text) { bag.push(p); continue; }
    seq.push(p);
  }
  return seq.map((p) => ({
    lensId: p.lensId,
    prompt: p.text,
    options: shuffle(ORDER), // all five lenses, shuffled
  }));
}

// ─── Mode switch ──────────────────────────────────────────────
function ModeSwitch({ view, setView }) {
  return (
    <div className="mode-switch">
      <button className={`mode-tab ${view === "reference" ? "active" : ""}`} onClick={() => setView("reference")}>
        <span className="mode-idx">01</span>
        <span className="mode-label">Reference</span>
        <span className="mode-desc">The five lenses, explained</span>
      </button>
      <button className={`mode-tab ${view === "quiz" ? "active" : ""}`} onClick={() => setView("quiz")}>
        <span className="mode-idx">02</span>
        <span className="mode-label">Self-Test</span>
        <span className="mode-desc">Name the lens · leaderboard</span>
      </button>
    </div>
  );
}

// ─── Reference view ───────────────────────────────────────────
function ReferenceView() {
  const [zoom, setZoom] = useState(null); // lens object whose poster is enlarged

  useEffect(() => {
    if (!zoom) return;
    const onKey = (e) => { if (e.key === "Escape") setZoom(null); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [zoom]);

  return (
    <div className="ref-wrap">
      {zoom && (
        <div className="lightbox" onClick={() => setZoom(null)}>
          <img src={zoom.img} alt={`${zoom.name} Lens — full poster`} />
          <div className="lightbox-hint">CLICK ANYWHERE OR PRESS ESC TO CLOSE</div>
        </div>
      )}
      {LENSES.map((l, i) => (
        <section className="lens-card" key={l.id} data-lens={l.id}>
          <button className="lens-poster" onClick={() => setZoom(l)} aria-label={`Enlarge the ${l.name} lens poster`}>
            <img src={l.img} alt={`${l.name} Lens — illustrated poster`} loading="lazy" />
            <span className="poster-zoom">⤢ ENLARGE</span>
          </button>
          <div className="lens-body">
          <div className="lens-top">
            <span className="lens-num">L{pad(i + 1)}</span>
            <div>
              <h3 className="lens-name">{l.name}</h3>
              <p className="lens-tag">{l.tagline}</p>
            </div>
          </div>

          <p className="lens-def">{l.def}</p>

          <div className="lens-cols">
            <div className="lens-block">
              <div className="lb-head">What it asks</div>
              <ul className="lb-list">
                {l.asks.map((a, j) => <li key={j}>{a}</li>)}
              </ul>
            </div>
            <div className="lens-block">
              <div className="lb-head">What to look for</div>
              <div className="chip-tags">
                {l.look.map((x, j) => <span className="tag" key={j}>{x}</span>)}
              </div>
              <div className="lb-head" style={{ marginTop: "var(--s-5)" }}>How to apply</div>
              <ol className="lb-steps">
                {l.howTo.map((s, j) => <li key={j}>{s}</li>)}
              </ol>
            </div>
          </div>

          <div className="lens-terms">
            <div className="lb-head">Key terms</div>
            <dl className="terms-grid">
              {l.terms.map(([t, d], j) => (
                <div className="term" key={j}>
                  <dt>{t}</dt>
                  <dd>{d}</dd>
                </div>
              ))}
            </dl>
          </div>

          <div className="lens-example">
            <div className="ex-rec"><span className="rec-dot" /><span className="rec-text">WORKED EXAMPLE · {l.exampleText}</span></div>
            <p>{l.example}</p>
          </div>
          </div>{/* lens-body */}
        </section>
      ))}
    </div>
  );
}

// ─── Leaderboard ──────────────────────────────────────────────
function Leaderboard({ rows, stats, loading, highlightId }) {
  const [filter, setFilter] = useState("all");
  if (!FB_READY) {
    return (
      <div className="lb-panel quiet">
        <div className="lb-head-row"><span className="rec-dot" /> LEADERBOARD</div>
        <p className="lb-na">Offline — the leaderboard goes live once Firebase is connected.</p>
      </div>
    );
  }
  const filtered = (rows || []).filter((r) => filter === "all" || r.mode === filter).slice(0, 20);
  const total = stats ? stats.totalAttempts || 0 : 0;
  const avg = stats && total ? Math.round((stats.scoreSumPct || 0) / total) : null;
  const topMiss = stats
    ? Object.entries(stats.misidentified || {}).sort((a, b) => b[1] - a[1])[0]
    : null;

  return (
    <div className="lb-panel">
      <div className="lb-head-row">
        <span><span className="rec-dot" /> LEADERBOARD {loading ? "· syncing…" : ""}</span>
        <div className="lb-filter">
          {["all", "concept", "applied"].map((f) => (
            <button key={f} className={`lb-fbtn ${filter === f ? "active" : ""}`} onClick={() => setFilter(f)}>
              {f === "all" ? "ALL" : f.toUpperCase()}
            </button>
          ))}
        </div>
      </div>

      <div className="lb-stats">
        <div className="lb-stat"><span className="lb-stat-n">{total.toLocaleString()}</span><span className="lb-stat-l">Attempts</span></div>
        <div className="lb-stat"><span className="lb-stat-n">{avg == null ? "—" : avg + "%"}</span><span className="lb-stat-l">Avg score</span></div>
        <div className="lb-stat"><span className="lb-stat-n sm">{topMiss ? BY_ID[topMiss[0]].name.replace(" (Marxist)", "") : "—"}</span><span className="lb-stat-l">Trickiest lens</span></div>
      </div>

      {filtered.length === 0 && <p className="lb-na">No scores yet — be the first on the board.</p>}
      {filtered.length > 0 && (
        <ol className="lb-list-ol">
          {filtered.map((r, i) => (
            <li key={r.id} className={`lb-row ${r.id === highlightId ? "me" : ""} rank-${i + 1}`}>
              <span className="lb-rank">{pad(i + 1)}</span>
              <span className="lb-name">{r.name}</span>
              <span className="lb-date">{fmtDate(r.ts)}</span>
              <span className="lb-mode">{r.mode}</span>
              <span className="lb-pct">{r.pct}%</span>
            </li>
          ))}
        </ol>
      )}
    </div>
  );
}

// ─── Quiz view ────────────────────────────────────────────────
function QuizView() {
  const [phase, setPhase] = useState("setup"); // setup | playing | results
  const [mode, setMode] = useState("concept");
  const [quiz, setQuiz] = useState([]);
  const [idx, setIdx] = useState(0);
  const [picked, setPicked] = useState(null);
  const [answers, setAnswers] = useState([]);

  const [rows, setRows] = useState(null);
  const [stats, setStats] = useState(null);
  const [lbLoading, setLbLoading] = useState(false);

  const [initials, setInitials] = useState("");
  const [submitState, setSubmitState] = useState("idle"); // idle | saving | done | error
  const [myId, setMyId] = useState(null);

  const loadBoard = useCallback(async () => {
    if (!FB_READY) return;
    setLbLoading(true);
    const [r, s] = await Promise.all([fetchLeaderboard(), fetchLensStats()]);
    if (r) setRows(r);
    if (s) setStats(s);
    setLbLoading(false);
  }, []);

  useEffect(() => { loadBoard(); }, [loadBoard]);

  const start = (m) => {
    setMode(m);
    setQuiz(buildQuiz(m));
    setIdx(0);
    setPicked(null);
    setAnswers([]);
    setInitials("");
    setSubmitState("idle");
    setMyId(null);
    setPhase("playing");
  };

  const choose = (lensId) => {
    if (picked != null) return;
    setPicked(lensId);
    const cur = quiz[idx];
    setAnswers((a) => [...a, { lensId: cur.lensId, chosenId: lensId, correct: lensId === cur.lensId }]);
  };

  const next = () => {
    if (idx + 1 < quiz.length) { setIdx(idx + 1); setPicked(null); }
    else setPhase("results");
  };

  const correctCount = answers.filter((a) => a.correct).length;
  const pct = quiz.length ? Math.round((correctCount / quiz.length) * 100) : 0;

  const doSubmit = async () => {
    const name = cleanInitials(initials);
    if (name.length < 2) { setSubmitState("short"); return; }
    if (isBad(name)) { setSubmitState("bad"); return; }
    setSubmitState("saving");
    const wrongLensIds = answers.filter((a) => !a.correct).map((a) => a.lensId);
    const id = await submitScore({ name, pct, correct: correctCount, total: quiz.length, mode, wrongLensIds });
    if (id) {
      setMyId(id);
      setSubmitState("done");
      await loadBoard();
    } else {
      setSubmitState("error");
    }
  };

  // ── setup ──
  if (phase === "setup") {
    return (
      <div className="quiz-wrap">
        <div className="setup-intro">
          <h3 className="setup-title">Name the lens.</h3>
          <p className="setup-sub">
            {QUESTIONS_PER_QUIZ} prompts — each shows a critical move, and you pick which of the five lenses
            it belongs to. No login. Post your score to the leaderboard with your initials.
          </p>
        </div>
        <div className="level-cards">
          <button className="level-card" onClick={() => start("concept")}>
            <span className="lc-tag">MODE 01</span>
            <span className="lc-name">Concept</span>
            <span className="lc-desc">Identify the lens from its core focus and guiding questions. Learn the five.</span>
            <span className="lc-go">BEGIN →</span>
          </button>
          <button className="level-card adv" onClick={() => start("applied")}>
            <span className="lc-tag">MODE 02</span>
            <span className="lc-name">Applied</span>
            <span className="lc-desc">Identify the lens from a real excerpt or piece of analysis. Exam-level.</span>
            <span className="lc-go">BEGIN →</span>
          </button>
        </div>
        <Leaderboard rows={rows} stats={stats} loading={lbLoading} highlightId={myId} />
      </div>
    );
  }

  // ── results ──
  if (phase === "results") {
    const missed = answers.filter((a) => !a.correct);
    const grade = pct >= 90 ? "EXCELLENT" : pct >= 75 ? "STRONG" : pct >= 50 ? "GETTING THERE" : "KEEP STUDYING";
    return (
      <div className="quiz-wrap">
        <div className="results">
          <div className="res-score-card">
            <div className="ex-rec res-rec"><span className="rec-dot" /><span className="rec-text">RESULT</span></div>
            <div className="res-pct">{pct}<span className="res-pct-sm">%</span></div>
            <div className="res-frac">{correctCount} / {quiz.length} correct</div>
            <div className="res-grade">{grade}</div>
            <div className="res-mode">{mode === "concept" ? "Concept" : "Applied"} mode</div>
          </div>

          {/* Submit to leaderboard */}
          {FB_READY && (
            <div className="submit-box">
              {submitState !== "done" ? (
                <div className="submit-row">
                  <label className="submit-lab">POST TO LEADERBOARD</label>
                  <div className="submit-input-row">
                    <input
                      className="initials-input"
                      value={initials}
                      onChange={(e) => { setInitials(cleanInitials(e.target.value)); if (submitState !== "saving") setSubmitState("idle"); }}
                      placeholder="AB"
                      maxLength={4}
                      aria-label="Your initials"
                    />
                    <button className="btn-primary" onClick={doSubmit} disabled={submitState === "saving"}>
                      {submitState === "saving" ? "SAVING…" : "SUBMIT"}
                    </button>
                  </div>
                  <div className="submit-hint">
                    {submitState === "short" && <span className="warnmsg">Enter 2–4 letters.</span>}
                    {submitState === "bad" && <span className="warnmsg">Please choose different initials.</span>}
                    {submitState === "error" && <span className="warnmsg">Couldn’t save — try again.</span>}
                    {(submitState === "idle") && <span>2–4 letters, shown publicly on the board.</span>}
                  </div>
                </div>
              ) : (
                <div className="submit-done">✓ Posted as <strong>{cleanInitials(initials)}</strong> — you’re on the board.</div>
              )}
            </div>
          )}

          {missed.length > 0 && (
            <div className="res-review">
              <div className="rr-head">REVIEW · {missed.length} missed</div>
              {missed.map((a, i) => (
                <div className="rr-row" key={i}>
                  <div className="rr-correct">
                    <span className="rr-tick">✓</span>
                    <span className="rr-name">{BY_ID[a.lensId].name}</span>
                    <span className="rr-def">{BY_ID[a.lensId].tagline}</span>
                  </div>
                  <div className="rr-wrong">you chose <span>{BY_ID[a.chosenId].name}</span></div>
                </div>
              ))}
            </div>
          )}
          {missed.length === 0 && <div className="res-perfect">Perfect read — every lens identified correctly.</div>}

          <div className="res-actions">
            <button className="btn-primary" onClick={() => start(mode)}>RETAKE · {mode === "concept" ? "CONCEPT" : "APPLIED"}</button>
            <button className="btn-ghost" onClick={() => start(mode === "concept" ? "applied" : "concept")}>
              TRY {mode === "concept" ? "APPLIED" : "CONCEPT"}
            </button>
            <button className="btn-ghost" onClick={() => setPhase("setup")}>← SETUP</button>
          </div>

          <Leaderboard rows={rows} stats={stats} loading={lbLoading} highlightId={myId} />
        </div>
      </div>
    );
  }

  // ── playing ──
  const cur = quiz[idx];
  const answered = picked != null;
  return (
    <div className="quiz-wrap">
      <div className="quiz-play">
        <div className="qp-head">
          <button className="back-btn" onClick={() => setPhase("setup")}>
            <span className="arr">←</span><span>END</span>
          </button>
          <div className="qp-meta">
            <span className="qp-mode">{mode === "concept" ? "CONCEPT" : "APPLIED"}</span>
            <span className="qp-count">{pad(idx + 1)} / {pad(quiz.length)}</span>
          </div>
        </div>

        <div className="qp-progress"><div className="qp-progress-fill" style={{ width: `${(idx / quiz.length) * 100}%` }} /></div>

        <div className="qp-evidence">
          <div className="qp-q">WHICH LENS IS THIS?</div>
          <p className="qp-prompt">{mode === "applied" ? "“" + cur.prompt + "”" : cur.prompt}</p>
        </div>

        <div className="qp-options five">
          {cur.options.map((lensId) => {
            const l = BY_ID[lensId];
            let cls = "opt";
            if (answered) {
              if (lensId === cur.lensId) cls += " correct";
              else if (lensId === picked) cls += " wrong";
              else cls += " dim";
            }
            return (
              <button key={lensId} className={cls} disabled={answered} onClick={() => choose(lensId)}>
                <span className="opt-name">{l.name}</span>
                {answered && lensId === cur.lensId && <span className="opt-ic">✓</span>}
                {answered && lensId === picked && lensId !== cur.lensId && <span className="opt-ic">✕</span>}
              </button>
            );
          })}
        </div>

        {answered && (
          <div className="qp-feedback">
            <p className="qp-fb-def"><strong>{BY_ID[cur.lensId].name}:</strong> {BY_ID[cur.lensId].def}</p>
            <button className="btn-primary qp-next" onClick={next}>
              {idx + 1 < quiz.length ? "NEXT →" : "SEE RESULTS →"}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── App ──────────────────────────────────────────────────────
function App() {
  const [view, setView] = useState("reference");
  return (
    <div>
      <ModeSwitch view={view} setView={setView} />
      {view === "reference" ? <ReferenceView /> : <QuizView />}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("lens-root")).render(<App />);
