/* === PHYSL 210 — Progress dashboard (wide two-column) =============================
   Reads the local analytics aggregate (data-stats.jsx) and the actually-loaded
   question/flashcard banks (denominators computed at runtime, never hard-coded).
   All visuals are hand-rolled SVG/CSS on the existing tokens. */

const { useState: useSPg, useEffect: useEPg, useRef: useRPg } = React;

const TBQ_P = (typeof TEXTBOOK_QUESTIONS !== 'undefined') ? TEXTBOOK_QUESTIONS : {};
const PG_MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const PG_DOW = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];

/* per-module totals from the loaded banks (course + textbook questions, cards) */
function pgTotals() {
  const per = {};
  let qTotal = 0, cTotal = 0;
  MODULES.forEach(m => {
    const qc = (QUESTIONS[m.id] || []).filter(q => !q.src || q.src === 'course').length;
    const qt = (TBQ_P[m.id] || []).length;
    const cc = (FLASHCARDS[m.id] || []).length;
    per[m.id] = { q: qc + qt, c: cc };
    qTotal += qc + qt; cTotal += cc;
  });
  return { per, qTotal, cTotal, total: qTotal + cTotal };
}

/* roll the per-item maps up into the numbers the page shows */
function pgAggregate(st, totals) {
  let qAnswered = 0, qCorrect = 0, uniqAttempted = 0, cardsKnown = 0, cReviewed = 0;
  const per = {};
  MODULES.forEach(m => {
    const mr = st.modules[m.id] || {};
    const qItems = ((mr.q || {}).items) || {};
    const cItems = ((mr.c || {}).items) || {};
    const qKeys = Object.keys(qItems), cKeys = Object.keys(cItems);
    const qMastered = qKeys.reduce((n, k) => n + (qItems[k] === 1 ? 1 : 0), 0);
    const cKnown = cKeys.reduce((n, k) => n + (cItems[k] === CARD_MAX_LEVEL ? 1 : 0), 0);  // strict: only top-level (Mastered) cards count
    qAnswered += (mr.q || {}).attempts || 0;
    qCorrect += (mr.q || {}).correct || 0;
    cReviewed += (mr.c || {}).attempts || 0;
    uniqAttempted += qKeys.length + cKeys.length;
    cardsKnown += cKnown;
    per[m.id] = {
      qAttempted: qKeys.length, qMastered, qTotal: totals.per[m.id].q,
      cAttempted: cKeys.length, cKnown, cTotal: totals.per[m.id].c,
    };
  });
  return { qAnswered, qCorrect, cReviewed, uniqAttempted, cardsKnown, per };
}

/* ---- headline stat card (count-up on mount, optional leading mark) ---- */
function StatCard({ value, suffix, label, accent, delay, mark }) {
  const shown = useCountUp(value);
  return (
    <div className="panel prog-stat fade" style={{ animationDelay: `${delay || 0}ms` }}>
      <div style={{ display:'flex', alignItems:'center', gap:8 }}>
        {mark}
        <div className="stat-num" style={{ fontSize: 38, lineHeight: 1, color: accent || 'var(--ink)' }}>
          {shown}{suffix ? <span style={{ fontSize: 21 }}>{suffix}</span> : null}
        </div>
      </div>
      <div className="prog-stat-lbl">{label}</div>
    </div>
  );
}

/* ---- last-14-days activity (hand-rolled SVG bar chart) ---- */
function ActivityChart({ days }) {
  const wrapRef = useRPg(null);
  const [w, setW] = useSPg(680);
  const [hi, setHi] = useSPg(null);
  const [k, setK] = useSPg(() => prefersReducedMotion() ? 1 : 0);

  useEPg(() => {
    const el = wrapRef.current; if (!el) return;
    const update = () => setW(el.clientWidth || 680);
    update();
    let ro;
    try { ro = new ResizeObserver(update); ro.observe(el); }
    catch (e) { try { window.addEventListener('resize', update); } catch (e2) {} }
    return () => { try { ro && ro.disconnect(); } catch (e) {} try { window.removeEventListener('resize', update); } catch (e) {} };
  }, []);

  useEPg(() => {
    if (prefersReducedMotion()) { setK(1); return; }
    let raf, start = null;
    const step = (ts) => {
      if (start == null) start = ts;
      const t = Math.min(1, (ts - start) / 800);
      setK(1 - Math.pow(1 - t, 3));
      if (t < 1) raf = requestAnimationFrame(step); else setK(1);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, []);

  const H = 188, padT = 12, padB = 26;
  const chartH = H - padT - padB;
  const n = days.length;
  const colW = w / n;
  const barW = Math.max(8, Math.min(28, colW * 0.56));
  const max = Math.max(1, ...days.map(d => d.total));
  const yBase = H - padB;

  const hovered = hi != null ? days[hi] : null;
  const hx = hi != null ? colW * hi + colW / 2 : 0;
  const hTopH = hovered ? (hovered.total / max) * chartH * k : 0;
  const hBarTop = yBase - hTopH;
  const hFlip = hBarTop < 70;
  const hTipTop = hFlip ? hBarTop + 8 : hBarTop - 8;
  const hTipTransform = hFlip ? 'translate(-50%, 0)' : 'translate(-50%, -100%)';

  return (
    <div className="panel prog-chart-panel">
      <div className="prog-sec-head">
        <div className="prog-sec-title">Activity · last 14 days</div>
        <div className="prog-legend">
          <span><i style={{ background: 'var(--coral)' }} />Questions</span>
          <span><i style={{ background: 'var(--p-sky-i)' }} />Cards</span>
        </div>
      </div>
      <div ref={wrapRef} className="prog-chart" style={{ position: 'relative' }}>
        <svg width="100%" height={H} role="img" aria-label="Items studied per day over the last 14 days">
          {days.map((d, i) => {
            const cx = colW * i + colW / 2;
            const qH = (d.q / max) * chartH * k;
            const cH = (d.c / max) * chartH * k;
            const isToday = i === n - 1;
            const active = hi === i;
            return (
              <g key={d.date} onMouseEnter={() => setHi(i)} onMouseLeave={() => setHi(h => h === i ? null : h)}
                onClick={() => setHi(h => h === i ? null : i)} style={{ cursor: 'pointer' }}>
                <rect x={cx - barW / 2} y={padT} width={barW} height={chartH} rx={barW / 2}
                  fill="var(--line-soft)" opacity={active ? 0.9 : 0.5} />
                {qH > 0 && <rect x={cx - barW / 2} y={yBase - qH} width={barW} height={qH} rx={Math.min(barW / 2, 5)} fill="var(--coral)" />}
                {cH > 0 && <rect x={cx - barW / 2} y={yBase - qH - cH} width={barW} height={cH} rx={Math.min(barW / 2, 5)} fill="var(--p-sky-i)" />}
                <text x={cx} y={H - 8} textAnchor="middle"
                  style={{ fontFamily: 'Quicksand', fontWeight: 700, fontSize: 11, fill: isToday ? 'var(--coral-deep)' : 'var(--muted)' }}>
                  {d.dom}
                </text>
              </g>
            );
          })}
        </svg>
        {hovered && (
          <div className="prog-tip" style={{ left: `${hx}px`, top: `${hTipTop}px`, transform: hTipTransform }}>
            <div className="prog-tip-date">{PG_DOW[hovered.dow]} {PG_MONTHS[parseInt(hovered.date.slice(5, 7), 10) - 1]} {hovered.dom}</div>
            <div className="prog-tip-row"><i style={{ background: 'var(--coral)' }} />{hovered.q} question{hovered.q === 1 ? '' : 's'}</div>
            <div className="prog-tip-row"><i style={{ background: 'var(--p-sky-i)' }} />{hovered.c} card{hovered.c === 1 ? '' : 's'}</div>
            {hovered.total === 0 && <div className="prog-tip-row" style={{ color: 'var(--muted)' }}>No activity</div>}
          </div>
        )}
      </div>
    </div>
  );
}

/* ---- accuracy split: course vs textbook ---- */
function SourceSplit({ src }) {
  const rows = [
    { key: 'course', label: 'Course questions', color: 'var(--coral)', d: src.course || {} },
    { key: 'textbook', label: 'Textbook questions', color: 'var(--p-sky-i)', d: src.textbook || {} },
  ];
  return (
    <div className="panel">
      <div className="prog-sec-title" style={{ marginBottom: 16 }}>Accuracy by source</div>
      {rows.map(r => {
        const att = r.d.attempts || 0, cor = r.d.correct || 0;
        const pct = att ? Math.round(cor / att * 100) : 0;
        return (
          <div key={r.key} className="prog-src-row">
            <div className="prog-track-top"><span>{r.label}</span><b style={{ color: r.color }}>{att ? pct + '%' : '—'}</b></div>
            <AnimatedBar pct={pct} color={r.color} track="var(--line-soft)" />
            <div className="prog-track-sub">{cor}/{att} correct</div>
          </div>
        );
      })}
    </div>
  );
}

/* ---- left rail: every module by mastery — started modules weakest-first,
   never-touched modules sink to the bottom (shared moduleMastery definition) ---- */
function MasteryRail({ go }) {
  const rows = MODULES.map(m => { const mm = moduleMastery(m.id); return { m, pct: mm.pct, attempted: mm.attempted }; });
  rows.sort((a, b) => {
    const ga = a.attempted > 0 ? 0 : 1, gb = b.attempted > 0 ? 0 : 1;
    if (ga !== gb) return ga - gb;      // untouched modules to the bottom
    return a.pct - b.pct;               // weakest first among started
  });

  return (
    <div className="panel prog-rail">
      <div className="prog-rail-head">
        <div className="prog-sec-title" style={{ fontSize: 16 }}>Mastery by module</div>
        <span style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.4px' }}>Weakest first</span>
      </div>
      {rows.map(({ m, pct, attempted }) => {
        const c = modColor(m.hue);
        return (
          <button key={m.id} className="prog-rail-row" onClick={() => go('practice', m.id)} title={`Practice ${m.name}`}>
            <div className="prog-rail-top">
              <span className="mod-emblem" style={{ width: 28, height: 28, borderRadius: 9, background: c.bg, color: c.ink }}><ModuleGlyph m={m} size={16} color={c.ink} /></span>
              <span className="prog-rail-name">{m.name}{m.current ? ' ·' : ''}{m.current ? <span style={{ color: 'var(--coral-deep)' }}> this week</span> : ''}</span>
              <span className="prog-rail-pct" style={{ color: attempted ? c.ink : 'var(--muted)' }}>{pct}%</span>
            </div>
            <AnimatedBar pct={pct} color={c.ink} track="var(--line-soft)" height={7} />
          </button>
        );
      })}
    </div>
  );
}

/* ---- empty state for brand-new users ---- */
function EmptyProgress({ go }) {
  return (
    <div className="panel fade" style={{ textAlign: 'center', padding: '56px 40px', maxWidth: 640, margin: '0 auto' }}>
      <div className="icon-badge" style={{ width: 64, height: 64, borderRadius: 18, background: 'var(--coral-tint)', margin: '0 auto' }}>
        {Icons.practice('var(--coral-deep)')}
      </div>
      <div className="disp" style={{ fontSize: 27, fontWeight: 600, marginTop: 18 }}>No progress yet</div>
      <div style={{ color: 'var(--muted)', fontSize: 16, marginTop: 10, lineHeight: 1.55 }}>
        Answer some practice questions and rate a few flashcards — your mastery, accuracy and study streak will start showing up here.
      </div>
      <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 26, flexWrap: 'wrap' }}>
        <button className="btn btn-lg btn-primary" onClick={() => go('practice')}>Start practicing →</button>
        <button className="btn btn-lg btn-ghost" onClick={() => go('cards')}>Review flashcards</button>
      </div>
    </div>
  );
}

/* ---- page ---- */
function ProgressPage({ go }) {
  const [resetOpen, setResetOpen] = useSPg(false);

  const st = liveStats();
  const totals = pgTotals();
  const agg = pgAggregate(st, totals);

  const completion = totals.total ? Math.round(agg.uniqAttempted / totals.total * 100) : 0;
  const accuracy = agg.qAnswered ? Math.round(agg.qCorrect / agg.qAnswered * 100) : 0;
  const streak = currentStreak(st);
  const days = lastNDays(14, st);
  const hasActivity = agg.uniqAttempted > 0 || agg.qAnswered > 0 || agg.cReviewed > 0;

  function confirmReset() {
    try { resetStats(); } catch (e) {}
    try { resetAllProgress(); } catch (e) { setResetOpen(false); }   // reloads on success
  }

  return (
    <div className="page wide fade">
      <div className="page-head" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 16, flexWrap: 'wrap' }}>
        <div>
          <div className="page-eyebrow">Progress</div>
          <h1 className="page-title">Your progress</h1>
        </div>
        {hasActivity && (
          <button className="btn btn-md btn-ghost prog-reset" onClick={() => setResetOpen(true)}>↺ Reset progress</button>
        )}
      </div>

      {!hasActivity ? (
        <EmptyProgress go={go} />
      ) : (
        <div className="prog-wide">
          <MasteryRail go={go} />

          <div className="prog-main">
            <div className="prog-stats">
              <StatCard value={streak} suffix={streak === 1 ? ' day' : ' days'} label="Study streak" accent="var(--coral-deep)" delay={0}
                mark={<StreakMark size={20} color="var(--coral-deep)" />} />
              <StatCard value={accuracy} suffix="%" label="Overall accuracy" accent="var(--p-mint-i)" delay={60} />
              <StatCard value={completion} suffix="%" label="Course explored" accent="var(--coral)" delay={120} />
              <StatCard value={agg.cardsKnown} label="Flashcards mastered" accent="var(--p-lilac-i)" delay={180} />
            </div>
            <div className="fade" style={{ animationDelay: '120ms' }}><ActivityChart days={days} /></div>
            <div className="fade" style={{ animationDelay: '200ms' }}><SourceSplit src={st.bySource || {}} /></div>
          </div>
        </div>
      )}

      <Modal open={resetOpen} onClose={() => setResetOpen(false)} labelledBy="reset-title" describedBy="reset-desc">
        <div id="reset-title" className="disp" style={{ fontSize: 23, fontWeight: 600 }}>Reset all progress?</div>
        <div id="reset-desc" style={{ color: 'var(--muted)', marginTop: 10, fontSize: 15, lineHeight: 1.55 }}>
          This permanently clears your study stats, per-module progress and streaks, and restarts the course from the beginning.
          Your personal notes are kept.
        </div>
        <div style={{ display: 'flex', gap: 12, justifyContent: 'flex-end', marginTop: 24 }}>
          <button className="btn btn-md btn-ghost" autoFocus onClick={() => setResetOpen(false)}>Cancel</button>
          <button className="btn btn-md btn-danger" onClick={confirmReset}>Reset everything</button>
        </div>
      </Modal>
    </div>
  );
}
window.ProgressPage = ProgressPage;
