// ═══ UR2D STANDINGS — POINTS PROGRESSION (season graph, broadcast style) ════════════════════════
// Same sources as the matrix: useStandings (order / teams / colours) + useRaceResults (driver points
// per round) + useConstructorRounds (team points per round). The season follows useSharedSeason, so it
// tracks the selector in the standings section above. Loads after ur2d-standings-broadcast.jsx.

const URSG_PALETTE = ['#F2F2F4', '#E10600', '#F2B83A', '#2E86FF', '#2BD99F', '#B06CFF', '#FF7A1A', '#4FD1E8', '#FF5FA2', '#9AA3AE'];
const ursgNorm = (s) => (typeof rosterNorm === 'function' ? rosterNorm(s) : String(s || '').toLowerCase().replace(/[^a-z0-9]/g, '')) || String(s || '').trim().toLowerCase();
// Dark team colours are lifted toward white so their lines read on the slate.
function ursgLift(hex) {
  const m = /^#?([0-9a-f]{6})$/i.exec(String(hex || '').trim());
  if (!m) return null;
  const n = parseInt(m[1], 16), rgb = [n >> 16, (n >> 8) & 255, n & 255];
  const lin = rgb.map((v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); });
  const lum = 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2], k = lum < 0.05 ? 0.5 : lum < 0.12 ? 0.32 : 0;
  return '#' + rgb.map((v) => Math.round(v + (255 - v) * k).toString(16).padStart(2, '0')).join('');
}
function ursgTicks(max) {
  const m = Math.max(10, max), raw = m / 4, p = Math.pow(10, Math.floor(Math.log10(raw)));
  const step = [1, 2, 2.5, 5, 10].map((f) => f * p).find((s) => s >= raw), top = Math.ceil(m / step) * step, ticks = [];
  for (let v = 0; v <= top + 1e-9; v += step) ticks.push(Math.round(v * 10) / 10);
  return { top, ticks };
}
const ursgMono = (color, size = 10) => ({ fontFamily: 'JetBrains Mono', fontSize: size, letterSpacing: '0.2em', textTransform: 'uppercase', color });

const URStandingsGraph = ({ league }) => {
  const L = LEAGUE_BY_ID[league];
  const seasons = getSeasons(league);
  const [seasonId] = useSharedSeason(league);
  const activeId = seasons.some((s) => s.id === seasonId) ? seasonId : ((seasons.find((s) => s.isCurrent) || {}).id || defaultDataSeasonId(seasons));
  const data = useStandings(league, activeId);
  const label = data.season && data.season.label;
  const results = useRaceResults(league, label);
  const conRounds = useConstructorRounds(league, label);
  const teamBased = !!(data.teams && data.teams.length);
  const [view, setView] = React.useState('drivers');
  const isCon = teamBased && view === 'teams';
  const numRaces = (data.season && data.season.numRaces) || 10;
  const sdef = ((UR2D_ADAPTERS[league] && UR2D_ADAPTERS[league].seasons) || []).find((s) => s.label === label);
  const perRound = !!(sdef && sdef.liveSource);
  const [waited, setWaited] = React.useState(false);
  const [pick, setPick] = React.useState(null), [hl, setHl] = React.useState(null), [at, setAt] = React.useState(null);
  React.useEffect(() => { setWaited(false); const t = setTimeout(() => setWaited(true), 9000); return () => clearTimeout(t); }, [league, label]);
  React.useEffect(() => { setPick(null); setHl(null); setAt(null); }, [league, label, isCon]);

  // Round labels — same index-aligned calendar resolution as the matrix.
  const calSeasons = typeof getCalendarSeasons === 'function' ? getCalendarSeasons() : [];
  const idx = seasons.filter((s) => !s.dbOnly).findIndex((s) => s.id === activeId);
  const cal = getCalendar(league, idx >= 0 && calSeasons[idx] ? calSeasons[idx].id : label) || {};
  const races = Array.from({ length: numRaces }, (_, i) => {
    const r = (cal.races || []).find((x) => x.round === i + 1);
    return { n: i + 1, code: r && typeof UR_GP_LOC3 !== 'undefined' ? UR_GP_LOC3[r.grandPrix] || null : null, gp: r ? r.grandPrix : null };
  });

  const teamByShort = {};
  (data.teams || []).forEach((t) => { teamByShort[t.short] = t; });
  const series = (() => {
    if (isCon) return (data.teams || []).map((t, i) => {
      const rr = conRounds[t.short] || {};
      return { key: 'T:' + t.short, name: t.name || t.short, team: t.short, pos: t.pos || i + 1, total: t.pts, color: t.color, per: races.map((r) => (rr[r.n] ? Number(rr[r.n].pts) || 0 : null)) };
    });
    const byName = {};
    Object.entries(results).forEach(([n, list]) => list.forEach((r) => { const k = ursgNorm(r.name); (byName[k] || (byName[k] = {}))[n] = Number(r.points) || 0; }));
    return (data.drivers || []).map((d, i) => {
      const rr = byName[ursgNorm(d.name)] || {}, tm = teamByShort[d.team];
      return { key: 'D:' + d.name + ':' + d.pos, name: d.name, team: d.team, pos: d.pos || i + 1, total: d.pts, color: tm ? tm.color : null, per: races.map((r) => (rr[r.n] != null ? rr[r.n] : null)) };
    });
  })();
  const raced = races.filter((r) => (isCon ? series.some((s) => s.per[r.n - 1]) : Object.prototype.hasOwnProperty.call(results, r.n) || Object.prototype.hasOwnProperty.call(results, String(r.n)))).map((r) => r.n);
  const last = raced.length ? Math.max.apply(null, raced) : 0;

  // Colours: team colour (lifted), palette for solo leagues; a team's second car is dashed.
  const seenTeam = {};
  series.forEach((s, i) => {
    s.col = ursgLift(s.color) || URSG_PALETTE[i % URSG_PALETTE.length];
    s.dash = !isCon && teamBased && s.team && seenTeam[s.team] ? '7 5' : null;
    if (s.team) seenTeam[s.team] = true;
    let c = 0;
    s.cum = [0].concat(races.slice(0, last).map((r) => (c += s.per[r.n - 1] || 0)));
  });
  const focus = pick || new Set(series.filter((s) => isCon || s.pos <= 5).map((s) => s.key));
  const r0 = at || last;
  const rank = (r) => series.slice().sort((a, b) => b.cum[r] - a.cum[r] || a.pos - b.pos);
  const now = r0 ? rank(r0) : [], before = {};
  if (r0 > 1) rank(r0 - 1).forEach((s, i) => { before[s.key] = i + 1; });
  const { top, ticks } = ursgTicks(Math.max(0, ...series.map((s) => s.cum[last] || 0)));
  const X = (i) => (i / numRaces) * 1000, Y = (v) => 1000 - (v / top) * 1000;
  const off = series.filter((s) => s.total != null && Math.abs((s.cum[last] || 0) - s.total) > 0.5).length;
  const toggleFocus = (k) => { const n = new Set(focus); if (n.has(k)) n.delete(k); else n.add(k); setPick(n); };

  if (data.status === 'loading' || !(data.drivers || []).length) return null; // the standings section above shows loading / no-data
  const toggle = teamBased ?
    <div style={{ display: 'inline-flex', border: '1px solid rgba(255,255,255,0.12)' }}>
      {[['drivers', 'Drivers'], ['teams', 'Constructors']].map(([id, lab], i) => {
        const on = (isCon ? 'teams' : 'drivers') === id;
        return <button key={id} onClick={() => setView(id)} style={{ all: 'unset', cursor: 'pointer', padding: '0 18px', height: 40, boxSizing: 'border-box', display: 'flex', alignItems: 'center', fontFamily: 'JetBrains Mono', fontSize: 11, fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', color: on ? COLORS.plateInk : COLORS.textMute, background: on ? COLORS.plate : 'transparent', borderRight: i === 0 ? '1px solid rgba(255,255,255,0.1)' : 'none' }}>{lab}</button>;
      })}
    </div> : null;
  const note = (txt) =>
    <div style={{ marginTop: 28, padding: '40px 32px', textAlign: 'center', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)' }}>
      <span style={{ ...ursgMono(COLORS.textMute, 11), letterSpacing: '0.14em', lineHeight: 1.7 }}>{txt}</span>
    </div>;
  const loading = perRound && !last && !waited;
  const PT = 10, PB = 48;

  return (
    <section data-screen-label="Final — Points progression" style={{ position: 'relative', overflow: 'hidden', borderTop: '1px solid rgba(255,255,255,0.06)', background: `${COLORS.slate} repeating-linear-gradient(115deg, rgba(255,255,255,0.016) 0 90px, transparent 90px 200px)` }}>
      <style>{`
        .ursg-grid{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:56px;align-items:start;margin-top:28px}
        @media (max-width:1240px){.ursg-grid{grid-template-columns:minmax(0,1fr)}}
        .ursg-row{all:unset;box-sizing:border-box;cursor:pointer;display:grid;grid-template-columns:28px 30px 20px minmax(0,1fr) 40px 54px;align-items:center;gap:8px;width:100%;height:32px;padding:0 14px;transition:background .12s ease}
        .ursg-row:hover{background:rgba(255,255,255,0.05)}
        .ursg-row.p1,.ursg-row.p1:hover{background:${COLORS.plate}}
      `}</style>
      <div aria-hidden style={{ position: 'absolute', right: -240, bottom: -280, width: 640, height: 640, borderRadius: '50%', background: `radial-gradient(circle, ${COLORS.red}1c 0%, transparent 62%)`, filter: 'blur(40px)', pointerEvents: 'none' }}></div>
      <div style={{ position: 'relative', padding: '56px 44px 80px' }}>
        <URSBTitle action={toggle}>Points Progression</URSBTitle>
        <div style={{ ...ursgMono(COLORS.textMute), marginTop: 14 }}>{L.name} · {label}{last ? ` · after round ${last} of ${numRaces}` : ''}</div>
        {!perRound ? note(`Round-by-round points aren't archived for ${label} yet. The table above has the final standings.`) :
        loading ?
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 18, padding: '90px 0' }}>
            <div style={{ display: 'flex', gap: 8 }}>{[0, 1, 2].map((i) => <span key={i} style={{ width: 10, height: 10, background: L.accent, borderRadius: '50%', animation: `ur-pulse 1.2s ease-in-out ${i * 0.2}s infinite` }}></span>)}</div>
            <span style={ursgMono(COLORS.textMute)}>Loading round results…</span>
          </div> :
        !last ? note(sdef && sdef.isCurrent ? 'No rounds raced yet — the graph starts after round 1.' : "Couldn't load this season's round results.") :
        <div className="ursg-grid">
          <div>
            <div style={{ position: 'relative', height: 'clamp(340px, 34vw, 560px)' }}>
              <div aria-hidden style={{ position: 'absolute', left: 0, top: PT, bottom: PB, width: 40 }}>
                {ticks.map((v) => <span key={v} style={{ position: 'absolute', right: 8, top: `${100 - (v / top) * 100}%`, transform: 'translateY(-50%)', fontFamily: 'JetBrains Mono', fontSize: 10, color: COLORS.textDim, fontVariantNumeric: 'tabular-nums' }}>{v}</span>)}
              </div>
              <div style={{ position: 'absolute', left: 48, right: 14, top: PT, bottom: PB }} onMouseLeave={() => setAt(null)}>
                {ticks.map((v) => <div key={v} aria-hidden style={{ position: 'absolute', left: 0, right: 0, top: `${100 - (v / top) * 100}%`, height: 1, background: v === 0 ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.06)' }}></div>)}
                {last < numRaces && <div aria-hidden style={{ position: 'absolute', top: 0, bottom: 0, right: 0, left: `${(last / numRaces) * 100}%`, background: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.03) 0 6px, transparent 6px 14px)' }}></div>}
                <svg viewBox="0 0 1000 1000" preserveAspectRatio="none" aria-hidden="true" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
                  {[...series.filter((s) => !focus.has(s.key)), ...series.filter((s) => focus.has(s.key) && s.key !== hl), ...series.filter((s) => s.key === hl)].map((s) => {
                    const on = focus.has(s.key), isHl = s.key === hl, dim = hl && !isHl;
                    return <path key={s.key} d={s.cum.map((v, i) => `${i ? 'L' : 'M'}${X(i)},${Y(v)}`).join(' ')} fill="none" vectorEffect="non-scaling-stroke" strokeLinejoin="round" strokeLinecap="round"
                      stroke={on || isHl ? s.col : 'rgba(255,255,255,0.14)'} strokeWidth={isHl ? 3.5 : on ? 2.25 : 1.25} strokeDasharray={s.dash || undefined} opacity={dim ? (on ? 0.3 : 0.5) : 1} style={{ transition: 'opacity .15s ease' }}></path>;
                  })}
                </svg>
                <div aria-hidden style={{ position: 'absolute', top: 0, bottom: 0, left: `${(r0 / numRaces) * 100}%`, width: 1, background: 'rgba(255,255,255,0.3)' }}></div>
                {series.filter((s) => focus.has(s.key) || s.key === hl).map((s) =>
                  <span key={s.key} aria-hidden style={{ position: 'absolute', left: `${(r0 / numRaces) * 100}%`, top: `${100 - (s.cum[r0] / top) * 100}%`, width: 10, height: 10, borderRadius: '50%', transform: 'translate(-50%,-50%)', background: s.col, boxShadow: `0 0 0 2px ${COLORS.slate}`, opacity: hl && s.key !== hl ? 0.35 : 1 }}></span>)}
                {races.slice(0, last).map((r) =>
                  <div key={r.n} onMouseEnter={() => setAt(r.n)} onClick={() => setAt(r.n)} style={{ position: 'absolute', top: 0, bottom: 0, left: `${((r.n - 0.5) / numRaces) * 100}%`, width: `${(1 / numRaces) * 100}%`, cursor: 'crosshair' }}></div>)}
              </div>
              <div style={{ position: 'absolute', left: 48, right: 14, bottom: 0, height: PB - 10 }}>
                {races.map((r) =>
                  <span key={r.n} style={{ position: 'absolute', left: `${(r.n / numRaces) * 100}%`, top: 6, transform: 'translateX(-50%)', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3, opacity: r.n > last ? 0.4 : 1 }}>
                    <span style={{ fontFamily: 'JetBrains Mono', fontSize: 10, color: r.n === r0 ? COLORS.red : COLORS.textDim }}>R{r.n}</span>
                    <span style={{ fontFamily: 'Archivo', fontWeight: 800, fontSize: 11, letterSpacing: '0.06em', color: r.n === r0 ? COLORS.text : COLORS.textMute }}>{r.code || '—'}</span>
                  </span>)}
              </div>
            </div>
            {off > 0 && <div style={{ ...ursgMono(COLORS.textDim), letterSpacing: '0.08em', textTransform: 'none', fontSize: 11, lineHeight: 1.6, marginTop: 16 }}>◆ {off} {off === 1 ? 'total differs' : 'totals differ'} from the table above — sprint, penalty or bonus points aren't split by round.</div>}
          </div>
          <div style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', padding: '18px 0 12px' }}>
            <div style={{ padding: '0 14px 14px', borderBottom: '1px solid rgba(255,255,255,0.08)', marginBottom: 6 }}>
              <div style={ursgMono(COLORS.red)}>◆ After round {r0} / {numRaces}</div>
              <div style={{ fontFamily: 'Archivo', fontWeight: 900, fontSize: 22, letterSpacing: '-0.01em', textTransform: 'uppercase', color: COLORS.text, marginTop: 8, lineHeight: 1.05 }}>{races[r0 - 1] && races[r0 - 1].gp || 'Round ' + r0}</div>
            </div>
            {now.map((s, i) => {
              const p = i + 1, was = before[s.key], d = was ? was - p : 0, on = focus.has(s.key), p1 = p === 1, gain = s.per[r0 - 1];
              const ink = p1 ? COLORS.plateInk : on || s.key === hl ? COLORS.text : COLORS.textDim;
              return (
                <button key={s.key} className={'ursg-row' + (p1 ? ' p1' : '')} onClick={() => toggleFocus(s.key)} onMouseEnter={() => setHl(s.key)} onMouseLeave={() => setHl(null)} aria-pressed={on} title={on ? 'Hide line' : 'Show line'}>
                  <span style={{ fontFamily: 'Archivo', fontWeight: 900, fontSize: 16, color: p1 ? COLORS.plateInk : COLORS.text, fontVariantNumeric: 'tabular-nums', textAlign: 'center' }}>{p}</span>
                  <span style={{ fontFamily: 'JetBrains Mono', fontSize: 10, color: d > 0 ? COLORS.green : d < 0 ? COLORS.red : p1 ? COLORS.plateMute : COLORS.textDim }}>{d > 0 ? '▲' + d : d < 0 ? '▼' + -d : '–'}</span>
                  <span aria-hidden style={{ height: 3, background: on ? (s.dash ? `repeating-linear-gradient(90deg, ${s.col} 0 5px, transparent 5px 8px)` : s.col) : p1 ? 'rgba(11,11,13,0.2)' : 'rgba(255,255,255,0.18)' }}></span>
                  <span style={{ fontFamily: 'Archivo', fontWeight: 800, fontSize: 13, letterSpacing: '0.04em', textTransform: 'uppercase', color: ink, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.name}</span>
                  <span style={{ fontFamily: 'JetBrains Mono', fontSize: 11, color: p1 ? COLORS.plateMute : COLORS.textDim, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{gain ? '+' + (Math.round(gain * 10) / 10) : '—'}</span>
                  <span style={{ fontFamily: 'Archivo', fontWeight: 900, fontSize: 16, color: p1 ? COLORS.plateInk : COLORS.text, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{Math.round(s.cum[r0] * 10) / 10}</span>
                </button>);
            })}
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, padding: '12px 14px 0', ...ursgMono(COLORS.textDim), letterSpacing: '0.12em', fontSize: 9 }}>
              <span>Hover a round · click a name to plot it</span>
              {pick && <button onClick={() => setPick(null)} style={{ all: 'unset', cursor: 'pointer', color: COLORS.textMute, borderBottom: `1px solid ${COLORS.red}` }}>Reset</button>}
            </div>
          </div>
        </div>}
      </div>
    </section>);
};

Object.assign(window, { URStandingsGraph });
