/* global React, ReactDOM */

const { useState, useEffect, useMemo, useRef } = React;

// ---------------- Shared bits ----------------
// `np` is mutated when fresh data arrives from data.js. Components close over
// this binding and re-render via App's setState when the data updates.
let np = window.NP_DATA;

function fmt(n) {
  if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M';
  if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K';
  return String(n);
}

function CountryFlag({ code }) {
  return <span className="country-tag">{code}</span>;
}

function WeaponGlyph({ id, size = 28 }) {
  // Stylized SVG silhouettes - rust-flavored, monochrome
  const s = size;
  const stroke = '#bce505';
  const paths = {
    // Rifles. Each has a distinctive silhouette:
    //   AK = angled banana mag dropping down + wood stock + gas tube on top
    //   LR-300 = AR-style straight mag + telescoping stock + picatinny rail
    //   Bolt  = bolt-action with prominent scope and short straight mag
    //   M39   = AK-like body with long scope rail
    //   L96   = long sniper with full-length scope
    //   Semi  = stubby semi-auto rifle, no mag protrusion
    ak47: <g><path d="M2 13 L10 13 L10 19 L2 21 Z" /><rect x="10" y="13" width="32" height="4" /><path d="M14 17 L17 17 L18 22 L14 22 Z" /><path d="M20 17 L28 17 L31 25 L22 25 Z" /><rect x="20" y="11" width="14" height="2" /><rect x="42" y="14" width="4" height="2" /><rect x="44" y="12" width="2" height="2" /></g>,
    lr300: <g><path d="M2 12 L10 12 L10 19 L2 19 Z" fill="none" /><rect x="10" y="13" width="32" height="4" /><rect x="18" y="11" width="18" height="2" /><path d="M16 17 L19 17 L20 22 L16 22 Z" /><rect x="22" y="17" width="8" height="6" /><rect x="42" y="14" width="4" height="2" /></g>,
    bolt: <g><rect x="2" y="14" width="44" height="3" /><rect x="14" y="9" width="20" height="3" fill="none" /><line x1="14" y1="10" x2="34" y2="10" strokeWidth="0.4" /><rect x="22" y="17" width="6" height="4" fill="none" /><path d="M2 17 L8 17 L8 21 L2 22 Z" /></g>,
    semi: <g><rect x="6" y="14" width="26" height="4" /><path d="M2 13 L6 13 L6 19 L2 20 Z" /><rect x="32" y="14" width="4" height="2" /></g>,
    l96: <g><rect x="2" y="14" width="46" height="3" /><rect x="12" y="9" width="28" height="3" fill="none" /><line x1="14" y1="10" x2="38" y2="10" strokeWidth="0.4" /><rect x="20" y="17" width="6" height="4" fill="none" /><path d="M2 17 L10 17 L10 21 L2 22 Z" /></g>,
    m39: <g><path d="M2 13 L10 13 L10 19 L2 21 Z" /><rect x="10" y="13" width="32" height="4" /><rect x="14" y="9" width="22" height="3" fill="none" /><path d="M14 17 L17 17 L18 22 L14 22 Z" /><rect x="22" y="17" width="6" height="5" fill="none" /><rect x="42" y="14" width="4" height="2" /></g>,
    // SMG
    mp5: <g><path d="M6 12 L26 12 L26 16 L20 16 L20 22 L12 22 L12 16 L6 16 Z" /><rect x="14" y="10" width="3" height="2" fill="none" /></g>,
    thompson: <g><path d="M4 13 L34 13 L34 17 L28 17 L28 22 L20 22 L20 17 L4 17 Z" /><circle cx="22" cy="20" r="2.5" fill="none" /></g>,
    customsmg: <g><path d="M6 13 L28 13 L28 16 L22 16 L22 22 L14 22 L14 16 L6 16 Z" /><rect x="10" y="11" width="4" height="2" fill="none" /></g>,
    // LMG
    m249: <g><path d="M2 13 L40 13 L40 18 L36 18 L36 22 L24 22 L24 18 L20 18 L20 16 L2 16 Z" /><circle cx="28" cy="20" r="2" fill="none" /></g>,
    hmlmg: <g><path d="M2 12 L46 12 L46 17 L42 17 L42 22 L26 22 L26 17 L20 17 L20 15 L2 15 Z" /><rect x="28" y="18" width="6" height="3" fill="none" /></g>,
    // Pistols
    python: <g><path d="M10 14 L24 14 L24 18 L20 18 L20 22 L14 22 L14 18 L10 18 Z" /><circle cx="14" cy="14" r="3" fill="none" /></g>,
    revolver: <g><path d="M14 14 L30 14 L30 18 L26 18 L26 22 L18 22 L18 18 L14 18 Z" /><circle cx="16" cy="16" r="3.5" fill="none" /></g>,
    m92: <g><path d="M10 13 L34 13 L34 17 L26 17 L26 22 L18 22 L18 17 L10 17 Z" /><rect x="12" y="11" width="3" height="2" fill="none" /></g>,
    semipistol: <g><path d="M12 14 L28 14 L28 18 L22 18 L22 22 L16 22 L16 18 L12 18 Z" /></g>,
    eoka: <g><path d="M16 14 L26 14 L26 17 L20 17 L20 21 L16 21 Z" /></g>,
    nailgun: <g><path d="M10 13 L26 13 L26 16 L22 16 L22 22 L14 22 L14 16 L10 16 Z" /><rect x="22" y="14" width="2" height="2" /></g>,
    // Shotguns
    spas12: <g><path d="M2 13 L40 13 L40 17 L36 17 L36 20 L20 20 L20 17 L8 17 L8 20 L4 20 L4 17 L2 17 Z" /><rect x="14" y="9" width="22" height="2" fill="none" /><line x1="22" y1="20" x2="22" y2="24" /></g>,
    pumpshotgun: <g><path d="M2 14 L46 14 L46 17 L40 17 L40 14 L26 14 L26 19 L18 19 L18 22 L8 22 L8 17 L2 17 Z" /><rect x="28" y="13" width="8" height="3" fill="none" /></g>,
    doublebarrel: <g><rect x="4" y="12" width="30" height="2" /><rect x="4" y="16" width="30" height="2" /><path d="M2 12 L4 12 L4 18 L2 18 Z M34 12 L40 12 L40 22 L30 22 L30 18 L34 18 Z" /></g>,
    waterpipe: <g><path d="M2 14 L34 14 L34 16 L26 16 L26 19 L20 19 L20 22 L12 22 L12 16 L2 16 Z" /></g>,
    // Bows / xbow / speargun
    bow: <g><path d="M8 6 Q24 16 8 26" fill="none" /><line x1="8" y1="6" x2="8" y2="26" /><line x1="10" y1="16" x2="40" y2="16" /></g>,
    crossbow: <g><line x1="10" y1="6" x2="10" y2="22" strokeWidth="1.4" /><path d="M6 14 L40 14 L40 16 L36 16 L36 19 L20 19 L20 16 L6 16 Z" fill="none" /><rect x="14" y="20" width="6" height="3" fill="none" /></g>,
    speargun: <g><path d="M30 13 L46 14 L30 15 Z" /><rect x="6" y="13" width="24" height="3" /><rect x="10" y="16" width="4" height="6" /></g>,
    spear: <g><path d="M4 16 L40 12 L46 14 L40 16 L4 18 Z" /></g>,
    // Knives / swords / clubs
    knife: <g><path d="M2 14 L2 17 L18 17 L18 14 Z" /><path d="M18 11 L40 14 L40 17 L18 20 Z" /></g>,
    sword: <g><rect x="2" y="14" width="6" height="2" /><rect x="8" y="11" width="2" height="8" /><path d="M10 14 L42 14 L46 15 L42 16 L10 16 Z" /></g>,
    mace: <g><rect x="2" y="14" width="22" height="2" /><circle cx="32" cy="15" r="6" fill="none" /><line x1="32" y1="9" x2="32" y2="21" /><line x1="26" y1="15" x2="38" y2="15" /></g>,
    // Tools
    jackhammer: <g><rect x="20" y="6" width="8" height="14" /><rect x="22" y="20" width="4" height="3" /><rect x="16" y="23" width="16" height="2" /><line x1="14" y1="10" x2="20" y2="10" /></g>,
    chainsaw: <g><rect x="2" y="13" width="28" height="3" fill="none" /><line x1="4" y1="11" x2="6" y2="11" /><line x1="8" y1="11" x2="10" y2="11" /><line x1="12" y1="11" x2="14" y2="11" /><line x1="16" y1="11" x2="18" y2="11" /><line x1="20" y1="11" x2="22" y2="11" /><rect x="30" y="10" width="14" height="10" /><circle cx="34" cy="15" r="1.5" fill="none" /></g>,
    hatchet: <g><rect x="6" y="14" width="22" height="2" /><path d="M28 9 L42 9 L46 16 L42 23 L28 23 L26 19 L26 13 Z" fill="none" /></g>,
    pickaxe: <g><rect x="14" y="13" width="2" height="14" /><path d="M2 8 L26 14 L24 18 L4 12 Z" fill="none" /></g>,
    hammer: <g><rect x="6" y="14" width="22" height="2" /><rect x="28" y="9" width="10" height="12" /><line x1="33" y1="9" x2="33" y2="21" /></g>,
    rock: <g><path d="M14 12 L22 7 L32 9 L36 16 L32 22 L20 22 L14 18 Z" fill="none" /></g>,
    torch: <g><rect x="22" y="14" width="3" height="10" /><path d="M18 10 L22 4 L26 10 L24 14 L20 14 Z" /></g>,
    // Launchers / explosives
    rocketlauncher: <g><rect x="2" y="13" width="36" height="4" /><rect x="38" y="11" width="4" height="8" fill="none" /><rect x="14" y="17" width="6" height="6" /></g>,
    grenadelauncher: <g><circle cx="20" cy="15" r="6" fill="none" /><circle cx="20" cy="15" r="2" fill="none" /><rect x="26" y="13" width="18" height="4" /><rect x="14" y="20" width="4" height="4" /></g>,
    snowballgun: <g><circle cx="40" cy="14" r="4" fill="none" /><path d="M6 13 L34 13 L34 17 L26 17 L26 22 L18 22 L18 17 L6 17 Z" /></g>,
    grenade: <g><circle cx="24" cy="18" r="5" /><rect x="22" y="9" width="4" height="4" /><line x1="26" y1="11" x2="34" y2="6" strokeWidth="1.2" /></g>,
    beancan: <g><rect x="14" y="9" width="20" height="14" rx="2" fill="none" /><line x1="14" y1="13" x2="34" y2="13" /><line x1="14" y1="19" x2="34" y2="19" /></g>,
    smoke: <g><circle cx="14" cy="17" r="4" fill="none" /><circle cx="22" cy="14" r="5" fill="none" /><circle cx="32" cy="17" r="4" fill="none" /></g>,
    c4: <g><rect x="10" y="12" width="22" height="11" /><line x1="32" y1="12" x2="36" y2="6" strokeWidth="1.2" /><circle cx="36" cy="6" r="1" /></g>,
    satchel: <g><path d="M14 12 L34 12 L36 22 L12 22 Z" fill="none" /><rect x="20" y="9" width="8" height="3" fill="none" /><line x1="14" y1="16" x2="34" y2="16" /></g>,
    rocket: <g><path d="M2 14 L36 14 L40 12 L46 16 L40 20 L36 18 L2 18 Z" /></g>,
    // Default
    custom: <g><path d="M6 14 L24 14 L24 18 L20 18 L20 22 L14 22 L14 18 L6 18 Z" /></g>,
  };
  return (
    <svg width={s * 1.7} height={s} viewBox="0 0 48 28" fill={stroke} stroke={stroke} strokeWidth="0.6" className="weapon-glyph">
      {paths[id] || paths.custom}
    </svg>
  );
}

function Sparkline({ data, color = '#bce505', width = 80, height = 22 }) {
  const max = Math.max(...data, 1);
  const pts = data.map((v, i) => `${(i / (data.length - 1)) * width},${height - (v / max) * height}`).join(' ');
  return (
    <svg width={width} height={height} className="spark">
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.4" />
      <polyline points={`${pts} ${width},${height} 0,${height}`} fill={color} opacity="0.12" stroke="none" />
    </svg>
  );
}

// ---------------- Top bar ----------------
function TopBar({ route, setRoute }) {
  return (
    <header className="topbar">
      <a className="brand" href="#" onClick={(e) => { e.preventDefault(); setRoute({ name: 'leaderboard' }); }}>
        <span className="brand-mark">NP</span>
        <span className="brand-name">Noobs Paradise</span>
        <span className="brand-sub">// LEADERBOARD</span>
      </a>
      <div className="top-actions">
        <nav className="nav-pills">
          <a href="#" className={route.name === 'leaderboard' ? 'on' : ''} onClick={(e) => { e.preventDefault(); setRoute({ name: 'leaderboard' }); }}>Leaderboard</a>
          <a href="steam://connect/147.135.7.27:28015">Server</a>
          <a href="/">VIP Store</a>
          <a href="https://discord.gg/zVAtSyAeXF">Discord</a>
        </nav>
        <div className="live-indicator"><span className="live-dot" /> {(np.meta && np.meta.online) || 0} / {(np.meta && np.meta.max_players) || 100} ONLINE</div>
      </div>
    </header>
  );
}

// ---------------- LEADERBOARD ----------------
function Podium({ top3, onPick, killsKey = 'kills' }) {
  // Order: 2nd left, 1st center, 3rd right
  const slots = [top3[1], top3[0], top3[2]];
  const heights = [180, 240, 150];
  const ranks = [2, 1, 3];

  return (
    <div className="podium">
      {slots.map((p, i) => p ? (
        <button key={p.id} className={`podium-slot rank-${ranks[i]}`} onClick={() => onPick(p)}>
          <div className="podium-card">
            <div className="podium-avatar"><div className="avatar-mono">{p.name.slice(0, 2).toUpperCase()}</div>
              {ranks[i] === 1 && <div className="crown">★ #1</div>}
            </div>
            <div className="podium-name">{p.name}</div>
            {p.clan && <div className="podium-clan">[{p.clan}]</div>}
            <div className="podium-stats">
              <div><span className="lbl">KILLS</span><strong>{fmt(p[killsKey] ?? p.kills)}</strong></div>
              <div><span className="lbl">RES</span><strong className="alt">{fmt(p.resources)}</strong></div>
            </div>
            <Sparkline data={p.sparkline} width={140} height={28} />
          </div>
          <div className="pedestal" style={{ height: heights[i] }}>
            <div className="pedestal-stripe" />
            <div className="pedestal-rank">#{ranks[i]}</div>
            <div className="pedestal-label">{['SILVER', 'GOLD', 'BRONZE'][i]}</div>
          </div>
        </button>
      ) : <div key={`empty-${i}`} className={`podium-slot rank-${ranks[i]} empty`} />)}
    </div>
  );
}

// Compute the scoped kills value for a player given the active scope.
// 'wipe' => current totals. '24h' => last day of sparkline.
// '7d' => sum of last 7 days. '24h'/'7d' values are bounded by the sparkline,
// so they reflect activity since the plugin started — partial during the
// first 14 days post-wipe. 'all' uses lifetime fields via projectAllTime.
function scopedKills(p, scope) {
  const sp = p.sparkline || [];
  if (scope === '24h') return sp.length ? sp[sp.length - 1] : 0;
  if (scope === '7d') return sp.slice(-7).reduce((s, v) => s + v, 0);
  return p.kills;
}

// Project lifetime fields onto the player so downstream sort/display reads
// them naturally when scope='all'. Falls back to current-wipe values for
// any field the backend hasn't started emitting yet.
function projectAllTime(p) {
  const lt = p.lifetime;
  if (!lt) return p;
  return {
    ...p,
    kills: lt.kills ?? p.kills,
    deaths: lt.deaths ?? p.deaths,
    kd: lt.kd ?? p.kd,
    headshotPct: lt.headshotPct ?? p.headshotPct,
    damageDealt: lt.damageDealt ?? p.damageDealt,
    playtime: lt.playtime ?? p.playtime,
    resources: lt.resources ?? p.resources,
    structuresBuilt: lt.structuresBuilt ?? p.structuresBuilt,
    basesRaided: lt.basesRaided ?? p.basesRaided,
    longestKill: lt.longestKill ?? p.longestKill,
    streak: lt.bestStreak ?? p.streak
  };
}

function Leaderboard({ setRoute }) {
  const [scope, setScope] = useState('wipe');
  const [sort, setSort] = useState('kills');
  const [query, setQuery] = useState('');
  const [visible, setVisible] = useState(25);

  const scoped = useMemo(() => {
    return np.players.map(p => {
      const proj = scope === 'all' ? projectAllTime(p) : p;
      return { ...proj, _scopedKills: scopedKills(proj, scope) };
    });
  }, [scope]);

  const filtered = useMemo(() => {
    let list = scoped.slice();
    if (query) list = list.filter(p =>
      p.name.toLowerCase().includes(query.toLowerCase()) ||
      (p.clan && p.clan.toLowerCase().includes(query.toLowerCase()))
    );
    if (sort === 'kills') {
      list.sort((a, b) => b._scopedKills - a._scopedKills);
    } else {
      list.sort((a, b) => b[sort] - a[sort]);
    }
    return list;
  }, [scoped, sort, query]);

  const top3 = filtered.slice(0, 3);
  const rest = filtered.slice(3, visible);
  const showScopedKills = scope === '24h' || scope === '7d';

  return (
    <main className="page leaderboard">
      <section className="page-head">
        <div className="page-head-text">
          <span className="kicker"><span className="status-dot" /> {(() => { if (scope === 'all') return 'ALL TIME // ACROSS WIPES'; if (scope === '24h') return 'LAST 24 HOURS'; if (scope === '7d') return 'LAST 7 DAYS'; const ws = np.meta && np.meta.wipe_started; if (!ws) return 'CURRENT WIPE'; const days = Math.max(1, Math.floor((Date.now() - ws * 1000) / 86400000)); return `CURRENT WIPE // DAY ${days}`; })()}</span>
          <h1>Top of the Trash Heap</h1>
          <p className="tagline">Live leaderboard for the Noobs Paradise 3X TRIO server. Click any operator to drill into their kill feed, weapons, and play patterns.</p>
        </div>
        <div className="page-head-stats">
          <div className="stat-pill"><span>TOTAL KILLS</span><strong>{fmt(np.players.reduce((s, p) => s + p.kills, 0))}</strong></div>
          <div className="stat-pill"><span>RESOURCES</span><strong className="alt">{fmt(np.players.reduce((s, p) => s + p.resources, 0))}</strong></div>
          <div className="stat-pill"><span>ACTIVE</span><strong className="warn">{np.players.filter(p => p.online).length}</strong></div>
        </div>
      </section>

      <div className="filter-bar">
        <div className="filter-group">
          <span className="filter-label">SCOPE</span>
          {[['wipe', 'Current Wipe'], ['24h', 'Last 24h'], ['7d', 'Last 7 Days'], ['all', 'All Time']].map(([k, l]) => (
            <button key={k} className={`chip-btn ${scope === k ? 'on' : ''}`} onClick={() => setScope(k)}>{l}</button>
          ))}
        </div>
        <div className="filter-group">
          <span className="filter-label">SORT</span>
          {[['kills', 'Kills'], ['resources', 'Resources'], ['kd', 'K/D'], ['playtime', 'Playtime'], ['damageDealt', 'Damage']].map(([k, l]) => (
            <button key={k} className={`chip-btn ${sort === k ? 'on' : ''}`} onClick={() => setSort(k)}>{l}</button>
          ))}
        </div>
        <div className="filter-search">
          <span className="search-prompt">$</span>
          <input placeholder="search operator or clan..." value={query} onChange={(e) => setQuery(e.target.value)} />
        </div>
      </div>

      <Podium top3={top3} onPick={(p) => setRoute({ name: 'player', id: p.id })} killsKey={showScopedKills ? '_scopedKills' : 'kills'} />

      <div className="lb-table-wrap panel">
        <div className="stripe" />
        <div className="lb-table">
          <div className="lb-row lb-head">
            <span>RANK</span>
            <span>OPERATOR</span>
            <span>K / D</span>
            <span>KILLS</span>
            <span>RESOURCES</span>
            <span>PLAYTIME</span>
            <span>HS%</span>
            <span>STREAK</span>
            <span>FAV</span>
            <span>STATUS</span>
          </div>
          {rest.map((p, idx) => (
            <button key={p.id} className="lb-row" onClick={() => setRoute({ name: 'player', id: p.id })}>
              <span className="rank-cell">#{idx + 4}</span>
              <span className="op-cell">
                <span className="avatar-mini">{p.name.slice(0, 2).toUpperCase()}</span>
                <span className="op-name">
                  <span className="op-primary">{p.name} {p.clan && <span className="op-clan">[{p.clan}]</span>}</span>
                  <span className="op-meta">{p.country ? (<><CountryFlag code={p.country} /> · </>) : null}{fmt(p.damageDealt)} DMG</span>
                </span>
              </span>
              <span className="num"><strong>{p.kd}</strong></span>
              <span className="num">{fmt(showScopedKills ? p._scopedKills : p.kills)}<Sparkline data={p.sparkline} width={48} height={14} /></span>
              <span className="num alt">{fmt(p.resources)}</span>
              <span className="num">{p.playtime}h</span>
              <span className="num">{p.headshotPct}%</span>
              <span className={`streak ${p.streak >= 5 ? 'hot' : ''}`}>{p.streak >= 5 ? `🔥 ${p.streak}` : p.streak}</span>
              <span className="fav-cell"><WeaponGlyph id={p.favWeapon} size={20} /></span>
              <span className={`status ${p.online ? 'on' : 'off'}`}>
                <span className="status-dot" />{p.online ? 'LIVE' : 'OFFLINE'}
              </span>
            </button>
          ))}
        </div>
        {visible < filtered.length && (
          <button className="load-more" onClick={() => setVisible(v => v + 25)}>LOAD MORE OPERATORS [{filtered.length - visible} REMAINING]</button>
        )}
      </div>
    </main>
  );
}

// ---------------- PLAYER OVERVIEW ----------------
function Heatmap({ data }) {
  // Polar/radial 24x7 — 7 concentric rings (days), 24 wedges (hours)
  const size = 360;
  const cx = size / 2, cy = size / 2;
  const innerR = 50;
  const outerR = 168;
  const ringW = (outerR - innerR) / 7;
  const days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'];

  const wedge = (d, h) => {
    const r0 = innerR + d * ringW;
    const r1 = r0 + ringW - 1.4;
    const a0 = (h / 24) * Math.PI * 2 - Math.PI / 2;
    const a1 = ((h + 1) / 24) * Math.PI * 2 - Math.PI / 2;
    const x0 = cx + r0 * Math.cos(a0), y0 = cy + r0 * Math.sin(a0);
    const x1 = cx + r1 * Math.cos(a0), y1 = cy + r1 * Math.sin(a0);
    const x2 = cx + r1 * Math.cos(a1), y2 = cy + r1 * Math.sin(a1);
    const x3 = cx + r0 * Math.cos(a1), y3 = cy + r0 * Math.sin(a1);
    return `M${x0},${y0} L${x1},${y1} A${r1},${r1} 0 0 1 ${x2},${y2} L${x3},${y3} A${r0},${r0} 0 0 0 ${x0},${y0} Z`;
  };

  const [hover, setHover] = useState(null);
  const peak = useMemo(() => {
    let best = { v: 0 };
    data.forEach((row, d) => row.forEach((v, h) => { if (v > best.v) best = { v, d, h }; }));
    return best;
  }, [data]);

  return (
    <div className="heatmap">
      <svg viewBox={`0 0 ${size} ${size}`} width="100%" className="heatmap-svg">
        {/* Hour ticks */}
        {Array.from({ length: 24 }, (_, h) => {
          const a = (h / 24) * Math.PI * 2 - Math.PI / 2;
          const x1 = cx + (outerR + 6) * Math.cos(a);
          const y1 = cy + (outerR + 6) * Math.sin(a);
          const x2 = cx + (outerR + 14) * Math.cos(a);
          const y2 = cy + (outerR + 14) * Math.sin(a);
          const labelX = cx + (outerR + 24) * Math.cos(a);
          const labelY = cy + (outerR + 24) * Math.sin(a);
          return (
            <g key={h}>
              <line x1={x1} y1={y1} x2={x2} y2={y2} stroke="#3b4348" strokeWidth="1" />
              {h % 3 === 0 && (
                <text x={labelX} y={labelY} fill="#9da69e" fontSize="9" fontFamily="ui-monospace, monospace" textAnchor="middle" dominantBaseline="middle">
                  {String(h).padStart(2, '0')}
                </text>
              )}
            </g>
          );
        })}
        {/* Wedges */}
        {data.map((row, d) => row.map((v, h) => {
          const opacity = Math.max(0.04, v / 100);
          const isHover = hover && hover.d === d && hover.h === h;
          const isPeak = peak.d === d && peak.h === h;
          return (
            <path
              key={`${d}-${h}`}
              d={wedge(d, h)}
              fill={v > 0 ? '#bce505' : '#161a1e'}
              fillOpacity={opacity}
              stroke={isHover ? '#bce505' : (isPeak ? '#f4b74a' : '#0c0e11')}
              strokeWidth={isHover || isPeak ? 1.5 : 0.4}
              onMouseEnter={() => setHover({ d, h, v })}
              onMouseLeave={() => setHover(null)}
            />
          );
        }))}
        {/* Day labels along top wedge of each ring */}
        {days.map((d, i) => {
          const r = innerR + i * ringW + ringW / 2;
          return (
            <text key={d} x={cx} y={cy - r + 3} fill="#5e6964" fontSize="8" fontFamily="ui-monospace, monospace" textAnchor="middle" fontWeight="900">
              {d}
            </text>
          );
        })}
        {/* Center hub */}
        <circle cx={cx} cy={cy} r={innerR - 4} fill="#0c0e11" stroke="#2b3035" strokeWidth="1" />
        <text x={cx} y={cy - 6} fill="#9da69e" fontSize="9" fontFamily="ui-monospace, monospace" textAnchor="middle" fontWeight="900">PEAK</text>
        <text x={cx} y={cy + 8} fill="#bce505" fontSize="14" fontFamily="ui-monospace, monospace" textAnchor="middle" fontWeight="900">
          {days[peak.d]} {String(peak.h).padStart(2, '0')}:00
        </text>
        <text x={cx} y={cy + 22} fill="#9da69e" fontSize="8" fontFamily="ui-monospace, monospace" textAnchor="middle">
          {peak.v}% ACTIVE
        </text>
      </svg>
      <div className="heatmap-legend">
        <span>LESS</span>
        <div className="legend-cells">
          {[0.1, 0.3, 0.5, 0.75, 1].map((o, i) => (
            <span key={i} className="legend-cell" style={{ background: `rgba(188,229,5,${o})` }} />
          ))}
        </div>
        <span>MORE</span>
        {hover && (
          <span className="heatmap-tip">
            {days[hover.d]} {String(hover.h).padStart(2, '0')}:00 — <strong>{hover.v}% active</strong>
          </span>
        )}
      </div>
    </div>
  );
}

function fmtAgo(sec) {
  if (sec < 5) return 'NOW';
  if (sec < 60) return sec + 's';
  if (sec < 3600) return Math.floor(sec / 60) + 'm';
  if (sec < 86400) return Math.floor(sec / 3600) + 'h';
  return Math.floor(sec / 86400) + 'd';
}

// `entries` is an array of killfeed entries from np.killfeed.
// Optional `viewerSteamId` filters to only kills involving that player and
// renders the verb relative to them ("killed" vs "killed by").
function KillFeed({ entries, viewerSteamId, title }) {
  const [paused, setPaused] = useState(false);
  const [tick, setTick] = useState(0);

  useEffect(() => {
    if (paused) return;
    const id = setInterval(() => setTick(t => t + 1), 1000);
    return () => clearInterval(id);
  }, [paused]);

  const list = useMemo(() => {
    let xs = (entries || []).slice();
    if (viewerSteamId) {
      xs = xs.filter(e => e.killerId === viewerSteamId || e.victimId === viewerSteamId);
    }
    return xs.slice(0, 14);
  }, [entries, viewerSteamId]);

  const nowSec = Math.floor(Date.now() / 1000);

  return (
    <div className="killfeed">
      <div className="killfeed-head">
        <h3>{title || 'LIVE KILL FEED'}</h3>
        <button className="kf-toggle" onClick={() => setPaused(p => !p)}>
          {paused ? '▶ RESUME' : '⏸ PAUSE'}
        </button>
      </div>
      {list.length === 0 ? (
        <p style={{ color: 'var(--muted)', padding: '12px 0', margin: 0 }}>
          {viewerSteamId ? 'No kills tracked for this player yet.' : 'No kills recorded yet.'}
        </p>
      ) : (
        <ul className="killfeed-list">
          {list.map((entry) => {
            const viewerIsKiller = viewerSteamId ? entry.killerId === viewerSteamId : true;
            const actor = viewerIsKiller ? (entry.killer || '—') : (entry.victim || '—');
            const target = viewerIsKiller ? (entry.victim || '—') : (entry.killer || '—');
            const ago = Math.max(0, nowSec - (entry.time || nowSec));
            return (
              <li key={entry.id} className={`kf-row ${viewerIsKiller ? 'kill' : 'death'}`}>
                <span className="kf-actor">{actor}</span>
                <span className="kf-arrow">
                  <WeaponGlyph id={entry.weapon?.id || 'custom'} size={20} />
                  {entry.distance > 0 && <span className="kf-dist">{entry.distance}m</span>}
                  {entry.headshot && <span className="kf-tag hs">HS</span>}
                  {entry.wounded && <span className="kf-tag wd">WND</span>}
                </span>
                <span className="kf-target">{target}</span>
                <span className="kf-time">{fmtAgo(ago)}</span>
              </li>
            );
          })}
        </ul>
      )}
    </div>
  );
}

function SessionsChart({ data }) {
  const sessions = data || [];
  const maxMin = Math.max(...sessions.map(s => s.minutes || 0), 1);
  const totalMin = sessions.reduce((a, b) => a + (b.minutes || 0), 0);
  const totalKills = sessions.reduce((a, b) => a + (b.kills || 0), 0);
  const totalDeaths = sessions.reduce((a, b) => a + (b.deaths || 0), 0);

  return (
    <div className="sessions">
      <div className="sessions-summary">
        <div><span className="lbl">14-DAY MIN</span><strong>{totalMin}</strong></div>
        <div><span className="lbl">14-DAY KILLS</span><strong>{totalKills}</strong></div>
        <div><span className="lbl">14-DAY DEATHS</span><strong className="warn">{totalDeaths}</strong></div>
      </div>
      <div className="sessions-bars">
        {sessions.map((s, i) => {
          const h = Math.max(2, Math.round((s.minutes / maxMin) * 64));
          const k = s.kills || 0;
          const d = s.deaths || 0;
          const isToday = i === sessions.length - 1;
          const dayLabel = i === sessions.length - 1 ? 'TODAY' : (sessions.length - 1 - i) + 'd';
          return (
            <div key={i} className="sb-col" title={`${s.minutes}m · ${k}K · ${d}D`}>
              <div className="sb-bar-track">
                <div className={`sb-bar ${isToday ? 'today' : ''}`} style={{ height: h + 'px' }} />
              </div>
              <div className="sb-kd">
                <span className="sb-k">{k || ''}</span>
                <span className="sb-d">{d || ''}</span>
              </div>
              <div className="sb-day">{dayLabel}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function WeaponBreakdown({ weapons }) {
  const max = Math.max(...weapons.map(w => w.kills));
  return (
    <div className="weapons">
      <ul className="weapons-list">
        {weapons.map(w => (
          <li key={w.id} className="weapon-row">
            <div className="weapon-header">
              <WeaponGlyph id={w.id} size={26} />
              <span className="weapon-name">{w.name}</span>
              <span className="weapon-kills"><strong>{w.kills}</strong> kills</span>
            </div>
            <div className="weapon-bar">
              <span className="weapon-fill" style={{ width: (w.kills / max * 100) + '%' }} />
            </div>
            <div className="weapon-meta">
              <span>ACC <strong>{w.accuracy}%</strong></span>
              <span>HS <strong>{w.headshotPct}%</strong></span>
              <span>DMG <strong>{fmt(w.dmg)}</strong></span>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
}

function ResourceTotals({ resources }) {
  const items = [
    { k: 'stone', label: 'Stone', color: '#9da69e' },
    { k: 'wood', label: 'Wood', color: '#a07a4a' },
    { k: 'metal', label: 'Metal Frags', color: '#7c8a92' },
    { k: 'sulfur', label: 'Sulfur', color: '#f4b74a' },
    { k: 'hqm', label: 'HQ Metal', color: '#e8eef0' },
    { k: 'cloth', label: 'Cloth', color: '#d6c5a8' },
    { k: 'scrap', label: 'Scrap', color: '#bce505' },
    { k: 'lowGrade', label: 'Low Grade', color: '#39c8b7' },
  ];
  const total = Object.values(resources).reduce((s, v) => s + v, 0);
  return (
    <div className="resources">
      <div className="resources-total">
        <span className="lbl">TOTAL GATHERED</span>
        <strong>{fmt(total)}</strong>
        <span className="lbl">UNITS</span>
      </div>
      <div className="resources-grid">
        {items.map(i => (
          <div key={i.k} className="resource-cell">
            <div className="resource-bar-bg">
              <div className="resource-bar" style={{
                height: ((resources[i.k] / Math.max(...Object.values(resources))) * 100) + '%',
                background: i.color
              }} />
            </div>
            <div className="resource-meta">
              <span className="resource-label">{i.label}</span>
              <span className="resource-num">{fmt(resources[i.k])}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function AchievementWall({ achievements }) {
  const earnedCount = achievements.filter(a => a.earned).length;
  return (
    <div className="achievements">
      <div className="ach-header">
        <span>UNLOCKED</span>
        <strong>{earnedCount} / {achievements.length}</strong>
        <div className="ach-progress"><span style={{ width: (earnedCount / achievements.length * 100) + '%' }} /></div>
      </div>
      <div className="ach-grid">
        {achievements.map(a => (
          <div key={a.id} className={`ach-tile rarity-${a.rarity} ${a.earned ? 'earned' : 'locked'}`}>
            <div className="ach-icon">
              {a.earned ? <span className="ach-glyph">✓</span> : <span className="ach-glyph locked">?</span>}
            </div>
            <div className="ach-name">{a.name}</div>
            <div className="ach-desc">{a.desc}</div>
            <div className="ach-rarity">{a.rarity.toUpperCase()}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function PlayerOverview({ playerId, setRoute }) {
  const player = np.players.find(p => p.id === playerId) || np.players[0];
  if (!player) {
    return (
      <main className="page player-overview">
        <button className="back-btn" onClick={() => setRoute({ name: 'leaderboard' })}>
          ← BACK TO LEADERBOARD
        </button>
        <section className="panel" style={{ padding: 32 }}>
          <p>Player not found.</p>
        </section>
      </main>
    );
  }

  const weapons = player.weapons || [];
  const resources = player.resourceBreakdown || { stone: 0, wood: 0, metal: 0, sulfur: 0, hqm: 0, cloth: 0, scrap: 0, lowGrade: 0 };
  const achievements = player.achievements || [];
  const heatmap = player.heatmap || Array.from({ length: 7 }, () => new Array(24).fill(0));
  const lastSeenStr = player.lastSeen || (player.online ? 'ONLINE NOW' : '—');
  const totalWeaponKills = weapons.reduce((s, w) => s + (w.kills || 0), 0);

  return (
    <main className="page player-overview">
      <button className="back-btn" onClick={() => setRoute({ name: 'leaderboard' })}>
        ← BACK TO LEADERBOARD
      </button>

      <section className="player-hero panel">
        <div className="stripe" />
        <div className="player-hero-inner">
          <div className="player-id">
            <div className="player-avatar">
              <div className="avatar-big">{player.name.slice(0, 2).toUpperCase()}</div>
              <div className={`live-pip ${player.online ? 'on' : 'off'}`}><span /></div>
            </div>
            <div className="player-id-text">
              <span className="kicker">RANK #{player.rank} · {player.clan ? `[${player.clan}]` : 'NO CLAN'}{player.country ? ` · ${player.country}` : ''}</span>
              <h1>{player.name}</h1>
              {player.bio && <p className="player-bio">{player.bio}</p>}
              <div className="player-id-meta">
                <span><span className="lbl">STEAM64</span><code>{player.steamId}</code></span>
                <span><span className="lbl">JOINED</span>{player.joined}</span>
                <span><span className="lbl">LAST SEEN</span><strong className={player.online ? 'live' : ''}>{lastSeenStr}</strong></span>
              </div>
            </div>
          </div>
          <div className="player-stat-rail">
            <div className="psr"><span>KILLS</span><strong>{fmt(player.kills)}</strong></div>
            <div className="psr"><span>DEATHS</span><strong className="warn">{fmt(player.deaths)}</strong></div>
            <div className="psr"><span>K/D</span><strong className="alt">{player.kd}</strong></div>
            <div className="psr"><span>PLAYTIME</span><strong>{player.playtime}h</strong></div>
            <div className="psr"><span>HEADSHOT</span><strong>{player.headshotPct}%</strong></div>
            <div className="psr"><span>LONGEST</span><strong>{player.longestKill}m</strong></div>
            <div className="psr"><span>RAIDS</span><strong>{player.basesRaided}</strong></div>
            <div className="psr"><span>STREAK</span><strong className={player.streak >= 5 ? 'live' : ''}>🔥 {player.streak}</strong></div>
          </div>
        </div>
      </section>

      <section className="overview-grid">
        <div className="panel module mod-killfeed">
          <div className="stripe" />
          <div className="module-inner">
            <KillFeed entries={np.killfeed} viewerSteamId={player.steamId} title="THIS PLAYER · KILL HISTORY" />
          </div>
        </div>

        <div className="panel module mod-heatmap">
          <div className="stripe" />
          <div className="module-inner">
            <div className="module-head">
              <h3>ACTIVE PLAYTIME RADAR</h3>
              <span className="module-sub">24×7 RADIAL · UTC</span>
            </div>
            <Heatmap data={heatmap} />
          </div>
        </div>

        <div className="panel module mod-weapons">
          <div className="stripe" />
          <div className="module-inner">
            <div className="module-head">
              <h3>WEAPON BREAKDOWN</h3>
              <span className="module-sub">{totalWeaponKills} KILLS RECORDED</span>
            </div>
            {weapons.length > 0
              ? <WeaponBreakdown weapons={weapons} />
              : <p style={{ color: 'var(--muted)', padding: '12px 0' }}>No weapon kills tracked yet.</p>}
          </div>
        </div>

        <div className="panel module mod-resources">
          <div className="stripe" />
          <div className="module-inner">
            <div className="module-head">
              <h3>RESOURCES GATHERED</h3>
              <span className="module-sub">CURRENT WIPE</span>
            </div>
            <ResourceTotals resources={resources} />
          </div>
        </div>

        <div className="panel module mod-achievements">
          <div className="stripe" />
          <div className="module-inner">
            <div className="module-head">
              <h3>BADGE WALL</h3>
              <span className="module-sub">SERVER ACHIEVEMENTS</span>
            </div>
            <AchievementWall achievements={achievements} />
          </div>
        </div>

        <div className="panel module mod-sessions">
          <div className="stripe" />
          <div className="module-inner">
            <div className="module-head">
              <h3>SESSION HISTORY</h3>
              <span className="module-sub">14-DAY · MINUTES PLAYED</span>
            </div>
            <SessionsChart data={player.sessions} />
          </div>
        </div>
      </section>
    </main>
  );
}

// ---------------- App shell ----------------
function LoadingScreen({ message, error }) {
  return (
    <div className="app">
      <main className="page" style={{ minHeight: '60vh', display: 'grid', placeItems: 'center' }}>
        <div className="panel" style={{ padding: '32px 40px', textAlign: 'center', maxWidth: 520 }}>
          <div className="stripe" />
          <h2 style={{ margin: '0 0 8px', color: 'var(--green)' }}>{error ? 'CONNECTION FAULT' : 'LINKING TO SERVER'}</h2>
          <p style={{ margin: 0, color: 'var(--muted)' }}>{error || (message || 'Polling stats endpoint…')}</p>
        </div>
      </main>
    </div>
  );
}

function EmptyServer({ setRoute, route }) {
  return (
    <div className="app">
      <TopBar route={route} setRoute={setRoute} />
      <main className="page" style={{ minHeight: '50vh', display: 'grid', placeItems: 'center' }}>
        <div className="panel" style={{ padding: '32px 40px', textAlign: 'center', maxWidth: 540 }}>
          <div className="stripe" />
          <h2 style={{ margin: '0 0 8px', color: 'var(--green)' }}>SERVER QUIET</h2>
          <p style={{ margin: 0, color: 'var(--muted)' }}>No tracked players yet. Stats will appear here once players start fighting.</p>
        </div>
      </main>
    </div>
  );
}

function App() {
  const [, forceTick] = useState(0);
  const [ready, setReady] = useState(window.NP_DATA_READY === true && !!window.NP_DATA);
  const [error, setError] = useState(window.NP_DATA_ERROR);
  const [route, setRoute] = useState({ name: 'leaderboard' });

  useEffect(() => {
    function onReady(e) {
      np = e.detail || window.NP_DATA;
      setError(null);
      setReady(true);
      forceTick(t => t + 1);
    }
    function onError(e) {
      setError(e.detail || 'Failed to load stats');
    }
    if (window.NP_DATA_READY && window.NP_DATA) {
      np = window.NP_DATA;
      setReady(true);
    }
    window.addEventListener('np-data-ready', onReady);
    window.addEventListener('np-data-error', onError);
    return () => {
      window.removeEventListener('np-data-ready', onReady);
      window.removeEventListener('np-data-error', onError);
    };
  }, []);

  if (!ready || !np) return <LoadingScreen error={error} />;

  const playerCount = (np.players && np.players.length) || 0;
  if (playerCount === 0) return <EmptyServer setRoute={setRoute} route={route} />;

  const meta = np.meta || {};
  const wipeStarted = meta.wipe_started ? new Date(meta.wipe_started * 1000) : null;
  const wipeDays = wipeStarted ? Math.max(1, Math.floor((Date.now() - wipeStarted.getTime()) / 86400000)) : null;

  return (
    <div className="app">
      <TopBar route={route} setRoute={setRoute} />
      {route.name === 'leaderboard'
        ? <Leaderboard setRoute={setRoute} />
        : <PlayerOverview playerId={route.id} setRoute={setRoute} />}
      <footer className="app-footer">
        <span>NP_LEADERBOARD{wipeDays ? ` · DAY ${wipeDays} OF WIPE` : ''} · POLLED EVERY 30s</span>
        <span>{meta.server_name || 'Noobs Paradise Network'} // 147.135.7.27:28015</span>
      </footer>
    </div>
  );
}

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