// PB progression SVG chart — tiny, area fill, dots, labelled endpoints.
// Only renders when 2+ verified PBs are known (either curated in data.js or
// learned at runtime from the speedrun.com API and persisted to localStorage).
function Progression({ history, wrMs }) {
  if (!history || history.length < 2) return null;

  const W = 640, H = 160, PAD_L = 44, PAD_R = 56, PAD_T = 18, PAD_B = 28;
  const maxMs = Math.max(...history.map(h => h.timeMs));
  const minMs = wrMs ? Math.min(wrMs, ...history.map(h => h.timeMs)) : Math.min(...history.map(h => h.timeMs));
  const rangeMs = maxMs - minMs || 1;

  const x = (i) => PAD_L + (i / (history.length - 1)) * (W - PAD_L - PAD_R);
  const y = (ms) => PAD_T + ((ms - minMs) / rangeMs) * (H - PAD_T - PAD_B);

  const pts = history.map((h, i) => `${x(i)},${y(h.timeMs)}`).join(" ");
  const area = `M${x(0)},${H - PAD_B} L${pts.replaceAll(" ", " L")} L${x(history.length - 1)},${H - PAD_B} Z`;

  const first = history[0], last = history[history.length - 1];
  const savedMs = first.timeMs - last.timeMs;
  const savedS = (savedMs / 1000).toFixed(2);

  const wrY = wrMs ? y(wrMs) : null;

  return (
    <div className="prog">
      <div className="prog__head">
        <span className="prog__kicker">PROGRESSION · {history.length} PBs</span>
        <span className="prog__delta">−{savedS}s from first</span>
      </div>
      <svg className="prog__svg" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" aria-hidden="true">
        <line x1={PAD_L} y1={H - PAD_B} x2={W - PAD_R} y2={H - PAD_B} stroke="var(--line)" strokeWidth="1" />

        {wrMs && (
          <>
            <line x1={PAD_L} y1={wrY} x2={W - PAD_R} y2={wrY}
                  stroke="var(--muted)" strokeDasharray="3 4" strokeWidth="1" opacity="0.8" />
            <text x={W - PAD_R + 6} y={wrY + 4} fontSize="10"
                  fontFamily="var(--font-mono)" fill="var(--muted)"
                  letterSpacing="1.2">WR</text>
          </>
        )}

        <path d={area} fill="var(--accent-soft)" />
        <polyline points={pts} fill="none" stroke="var(--accent)" strokeWidth="2"
                  strokeLinejoin="round" strokeLinecap="round" />
        {history.map((h, i) => {
          const isLast = i === history.length - 1;
          return (
            <g key={i}>
              <circle cx={x(i)} cy={y(h.timeMs)} r={isLast ? 5 : 3.5}
                      fill={isLast ? "var(--accent)" : "var(--bg)"}
                      stroke="var(--accent)" strokeWidth="2" />
            </g>
          );
        })}

        <text x={x(0)} y={y(first.timeMs) - 10} fontSize="10"
              fontFamily="var(--font-mono)" fill="var(--muted)"
              textAnchor="start" letterSpacing="0.5">{first.time}</text>
        <text x={x(history.length - 1)} y={y(last.timeMs) - 12} fontSize="11"
              fontFamily="var(--font-mono)" fill="var(--accent)"
              textAnchor="end" letterSpacing="0.5" fontWeight="600">{last.time}</text>

        {[0, Math.floor(history.length / 2), history.length - 1].map((i) => (
          <text key={i} x={x(i)} y={H - 8} fontSize="9"
                fontFamily="var(--font-mono)" fill="var(--muted)"
                textAnchor="middle" letterSpacing="0.4">
            {formatDate(history[i].date).replace(/ \d{4}$/, (m) => " '" + m.trim().slice(-2))}
          </text>
        ))}
      </svg>
    </div>
  );
}

// Leaderboard snapshot — top rows with the user's place highlighted.
// Top 3 get gold/silver/bronze medal icons in lieu of the "#1" / "#2" / "#3"
// labels — quickly readable as podium positions even at a glance.
function MedalIcon({ place }) {
  // Medal palettes — gold, silver, bronze. Drawn as inline SVG so they pick
  // up no external dependencies and recolour cleanly across the light/dark/
  // red themes via currentColor.
  const palettes = {
    1: { ribbon: "#C9302C", core: "#F2C744", rim: "#A87E16", glyph: "#5A4006" }, // gold
    2: { ribbon: "#1F2937", core: "#D9D9D9", rim: "#9AA0A6", glyph: "#3A3F47" }, // silver
    3: { ribbon: "#5A2E11", core: "#CD7F32", rim: "#7A4A1A", glyph: "#3A1F08" }, // bronze
  };
  const p = palettes[place];
  if (!p) return null;
  const label = place === 1 ? "Gold" : place === 2 ? "Silver" : "Bronze";
  return (
    <svg className={`medal medal--${place}`} viewBox="0 0 24 28"
         width="22" height="26" aria-label={`${label} medal`} role="img">
      {/* Ribbon — two diagonal triangles meeting at the medal */}
      <path d="M5 0 L11 0 L8 8 L2 8 Z" fill={p.ribbon} />
      <path d="M19 0 L13 0 L16 8 L22 8 Z" fill={p.ribbon} />
      <path d="M11 0 L13 0 L13 4 L11 4 Z" fill={p.ribbon} opacity="0.6" />
      {/* Medal disc */}
      <circle cx="12" cy="18" r="8" fill={p.core} stroke={p.rim} strokeWidth="1.4" />
      <circle cx="12" cy="18" r="5.5" fill="none" stroke={p.rim} strokeWidth="0.6" opacity="0.55" />
      {/* Place glyph — keep tiny and sharp at this size */}
      <text x="12" y="21.5" textAnchor="middle"
            fontFamily="ui-monospace, 'JetBrains Mono', monospace"
            fontSize="9" fontWeight="700" fill={p.glyph}>
        {place}
      </text>
    </svg>
  );
}

function Leaderboard({ rows, myTimeMs, myRank, myRankNote, totalRuns, updatedAt }) {
  if (!rows || !rows.length) return null;

  const meInList = rows.some(r => r.isMe);

  return (
    <div className="lb">
      <div className="lb__head">
        <span className="lb__kicker">LEADERBOARD · TOP {rows.length}{totalRuns ? ` / ${totalRuns}` : ""}</span>
        {myRank && <span className="lb__rank">MIRRORSEDGER · #{myRank}{totalRuns ? ` / ${totalRuns}` : ""}</span>}
      </div>
      <ol className="lb__list">
        {rows.map((r) => {
          const delta = r.timeMs - myTimeMs;
          const isMe = r.isMe;
          return (
            <li key={r.place + ":" + (r.url || r.runner)} className={`lb__row lb__row--p${r.place} ${isMe ? "lb__row--me" : ""}`}>
              <span className="lb__place">
                {r.place <= 3 ? <MedalIcon place={r.place} /> : `#${r.place}`}
              </span>
              <a className="lb__runner" href={r.url} target="_blank" rel="noreferrer">{r.runner}</a>
              <span className="lb__time">{r.time}</span>
              <span className={`lb__delta ${delta < 0 ? "is-faster" : delta > 0 ? "is-slower" : ""}`}>
                {isMe ? "—" : (delta === 0 ? "±0.00s" : (delta < 0 ? "−" : "+") + Math.abs(delta / 1000).toFixed(2) + "s")}
              </span>
            </li>
          );
        })}
        {/* If user isn't on the board, show a placeholder row below */}
        {!meInList && (() => {
          const wrMs = rows[0]?.timeMs ?? myTimeMs;
          const gap = myTimeMs - wrMs;
          return (
            <li className="lb__row lb__row--me lb__row--outside">
              <span className="lb__place">{myRank ? "#" + myRank : "—"}</span>
              <span className="lb__runner">mirrorsedger</span>
              <span className="lb__time">{msToShort(myTimeMs)}</span>
              <span className={`lb__delta ${gap < 0 ? "is-faster" : "is-slower"}`}>
                {myRankNote || (gap === 0 ? "—" : (gap < 0 ? "−" : "+") + Math.abs(gap / 1000).toFixed(2) + "s")}
              </span>
            </li>
          );
        })()}
      </ol>
      {updatedAt && (
        <div className="lb__note">
          {window.RUN_DATA.isLive ? "Live · " : "Snapshot · "}
          {window.RUN_DATA.isLive ? "via speedrun.com api" : "not live"}
          {" · updated "}{formatDate(updatedAt)}
        </div>
      )}
    </div>
  );
}

function msToShort(ms) {
  const totalS = ms / 1000;
  const m = Math.floor(totalS / 60);
  const s = Math.floor(totalS % 60);
  const mss = Math.round(ms % 1000);
  return `${m}:${String(s).padStart(2, "0")}.${String(mss).padStart(3, "0")}`;
}

// Compact gap formatter: "−1.91s" or "−1:04.30"
function fmtGap(ms, signed = true) {
  const sign = ms < 0 ? "−" : (signed ? "+" : "");
  const abs = Math.abs(ms);
  const totalS = abs / 1000;
  if (totalS < 60) return `${sign}${totalS.toFixed(2)}s`;
  const m = Math.floor(totalS / 60);
  const s = totalS - m * 60;
  return `${sign}${m}:${String(s.toFixed(2)).padStart(5, "0")}`;
}

// Stat strip: 4 KPIs above the leaderboard.
// Computes percentile, gap to next-better rank, time saved across history,
// and how long ago the PB was set.
function StatStrip({ run }) {
  const { timeMs, myRank, totalRuns, fullLeaderboard, history, date } = run;
  if (!myRank || !totalRuns) return null;

  // 1. Percentile — what % of runners you beat
  // (if rank 1 of 100, you beat 99% — i.e. (total - rank) / total)
  const beatPct = totalRuns > 1 ? Math.round(((totalRuns - myRank) / (totalRuns - 1)) * 100) : 100;
  const topPct = totalRuns > 1 ? Math.max(1, Math.round((myRank / totalRuns) * 100)) : 1;

  // 2. Gap to next rank above me
  let nextGap = null, nextRank = null;
  if (fullLeaderboard && fullLeaderboard.length) {
    // The runner immediately ahead of me — first row with place < myRank
    const ahead = [...fullLeaderboard]
      .filter(r => r.place < myRank)
      .sort((a, b) => b.place - a.place)[0];
    if (ahead) {
      nextGap = ahead.timeMs - timeMs; // negative — they're faster
      nextRank = ahead.place;
    }
  }

  // 3. Time improved across all PBs in history (only meaningful with 2+ entries)
  let savedMs = null;
  if (history && history.length >= 2) {
    savedMs = history[0].timeMs - history[history.length - 1].timeMs;
  }

  // 4. PB age
  const pbAge = date ? relativeTime(date) : null;

  // 5. Days since last improvement (uses the most-recent two history entries
  // to count the gap between PBs — small for an active run, large for stale).
  let dormantLabel = null, dormantSub = null;
  if (Array.isArray(history) && history.length >= 1) {
    const last = history[history.length - 1].date;
    const days = Math.floor((Date.now() - new Date(last).getTime()) / 86400000);
    if (days >= 730)      dormantLabel = `${Math.floor(days / 365)}y dormant`;
    else if (days >= 365) dormantLabel = `${Math.floor(days / 30)}mo dormant`;
    else if (days >= 30)  dormantLabel = `${Math.floor(days / 30)}mo`;
    else                  dormantLabel = `${days}d`;
    dormantSub = "since last PB";
  }

  const stats = [
    {
      label: "PERCENTILE",
      value: `TOP ${topPct}%`,
      sub: `beats ${beatPct}% of runners`,
    },
    nextRank ? {
      label: `GAP TO #${nextRank}`,
      value: fmtGap(nextGap, true),
      sub: "to climb one rank",
      accent: true,
    } : null,
    savedMs != null ? {
      label: "TIME SAVED",
      value: `−${(savedMs / 1000).toFixed(2)}s`,
      sub: `over ${history.length} PBs`,
      accent: true,
    } : null,
    dormantLabel ? {
      label: "ROUTE STATE",
      value: dormantLabel,
      sub: dormantSub,
    } : null,
    pbAge ? {
      label: "PB SET",
      value: pbAge,
      sub: formatDate(date),
    } : null,
  ].filter(Boolean);

  return (
    <div className="stat-strip">
      {stats.map((s) => (
        <div key={s.label} className="stat-cell">
          <div className="stat-cell__label">{s.label}</div>
          <div className={`stat-cell__value ${s.accent ? "is-accent" : ""}`}>{s.value}</div>
          {s.sub && <div className="stat-cell__sub">{s.sub}</div>}
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { Progression, Leaderboard, StatStrip, MedalIcon });
