// LAR API overview: Game Manager's dashboard. Online counts the live sockets, so it is players in the
// game right now, not an estimate.

function LarMigrationCard({ compact }) {
  const { data, error } = useLarLoad(() => larApi("migration"), []);
  if (error || !data) return null;
  const seen = data.counts?.seen || 0;
  const total = data.total || 0;
  const pct = total ? Math.min(100, Math.round((seen / total) * 100)) : data.status === "done" ? 100 : 0;
  const label = { idle: "Not started", running: "Running", paused: "Paused", done: "Finished", failed: "Stopped on errors" }[data.status] || data.status;

  return (
    <div className="card card-pad">
      <div className="card-head">
        <div><div className="card-title">PlayFab migration</div><div className="card-sub">{label}{total ? ` · ${fmt.num(seen)} of ${fmt.num(total)} PlayFab players` : ""}</div></div>
        {compact && <button className="btn btn-sm btn-ghost" onClick={() => { window.location.hash = "migration"; }}>Open</button>}
      </div>
      <div className="lar-progress mt-8"><div style={{width: pct + "%"}}/></div>
      <div className="h-stack gap-12 mt-12 wrap tiny">
        <span>Copied <strong>{fmt.num(data.counts?.imported)}</strong></span>
        <span>Refreshed <strong>{fmt.num(data.counts?.refreshed)}</strong></span>
        <span>Already here <strong>{fmt.num(data.counts?.skippedClaimed)}</strong></span>
        <span>No Meta link <strong>{fmt.num(data.counts?.unmapped)}</strong></span>
        <span style={{color: data.counts?.failed ? "var(--danger)" : undefined}}>Failed <strong>{fmt.num(data.counts?.failed)}</strong></span>
      </div>
    </div>
  );
}

const LAR_CCU_PERIODS = [["month", "This month"], ["weekend", "Weekend"], ["week", "7d"], ["day", "24h"]];

function larCcuTime(t, period) {
  const d = new Date(t);
  if (period === "day") return d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" });
  if (period === "weekend") return d.toLocaleString(undefined, { weekday: "short", hour: "2-digit" });
  return d.toLocaleDateString(undefined, { month: "short", day: "numeric" });
}

/** This period against the last, the previous one dashed. */
function LarComparisonChart({ current, previous, xLabels, curLabel, prevLabel, width = 1020, height = 220, labelFmt = v => v }) {
  const padL = 38, padR = 14, padT = 18, padB = 26;
  const w = width - padL - padR, h = height - padT - padB;
  const max = Math.max(...current, ...previous, 1) * 1.15;
  const [hover, setHover] = React.useState(null);
  const project = (data) => {
    const sx = w / (data.length - 1 || 1);
    return data.map((v, i) => [padL + i * sx, padT + h - (v / max) * h]);
  };
  const cur = project(current), prev = project(previous);
  const toPath = (pts) => pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
  const area = toPath(cur) + ` L${padL + w},${padT + h} L${padL},${padT + h} Z`;
  const ticks = [0, 1, 2, 3, 4].map(i => ({ y: padT + (h * i) / 4, v: max - (max * i) / 4 }));
  const onMove = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const f = (((e.clientX - rect.left) * (width / rect.width)) - padL) / w;
    setHover(f >= 0 && f <= 1 ? f : null);
  };
  const ci = hover != null ? Math.round(hover * (current.length - 1)) : null;
  const pi = hover != null ? Math.round(hover * (previous.length - 1)) : null;

  return (
    <div>
      <div className="h-stack gap-12" style={{marginBottom:6, fontSize:11, color:"var(--ink-2)"}}>
        <span className="h-stack gap-6"><span style={{width:14, height:3, background:"#10b981", borderRadius:2}}/>{curLabel}</span>
        <span className="h-stack gap-6"><span style={{width:14, borderTop:"2px dashed var(--ink-3)"}}/>{prevLabel}</span>
      </div>
      <svg viewBox={`0 0 ${width} ${height}`} onMouseMove={onMove} onMouseLeave={() => setHover(null)} style={{display:"block", width:"100%", height:"auto", aspectRatio:`${width} / ${height}`}}>
        <defs>
          <linearGradient id="lar-cmp-grad" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="#10b981" stopOpacity=".18"/>
            <stop offset="100%" stopColor="#10b981" stopOpacity="0"/>
          </linearGradient>
        </defs>
        {ticks.map((t, i) => (
          <g key={i}>
            <line x1={padL} y1={t.y} x2={padL + w} y2={t.y} stroke="var(--border)"/>
            <text x={padL - 8} y={t.y + 3.5} textAnchor="end" fontSize="10" fill="var(--ink-3)">{labelFmt(Math.round(t.v))}</text>
          </g>
        ))}
        <path d={area} fill="url(#lar-cmp-grad)"/>
        <path d={toPath(prev)} fill="none" stroke="var(--ink-3)" strokeWidth="1.6" strokeDasharray="4 3"/>
        <path d={toPath(cur)} fill="none" stroke="#10b981" strokeWidth="1.8"/>
        {ci != null && (
          <g>
            <line x1={cur[ci][0]} y1={padT} x2={cur[ci][0]} y2={padT + h} stroke="var(--ink)" strokeDasharray="2 3" strokeWidth="1"/>
            <circle cx={cur[ci][0]} cy={cur[ci][1]} r="3.5" fill="var(--surface)" stroke="#10b981" strokeWidth="1.5"/>
            <circle cx={cur[ci][0]} cy={prev[pi][1]} r="3" fill="var(--surface)" stroke="var(--ink-3)" strokeWidth="1.5"/>
            <g transform={`translate(${Math.min(cur[ci][0] + 8, padL + w - 100)}, ${padT})`}>
              <rect width="100" height="42" rx="6" fill="var(--ink)"/>
              <text x="8" y="13" fontSize="9" fill="var(--ink-3)" fontFamily="Kode Mono">{xLabels ? xLabels[ci] : ""}</text>
              <text x="8" y="26" fontSize="10.5" fill="var(--bg)" fontFamily="Kode Mono">{curLabel}: {labelFmt(current[ci])}</text>
              <text x="8" y="37" fontSize="10.5" fill="var(--ink-3)" fontFamily="Kode Mono">{prevLabel}: {labelFmt(previous[pi])}</text>
            </g>
          </g>
        )}
      </svg>
    </div>
  );
}

/** Concurrent players from Photon's dashboard (needs the Photon cookie vars on the Worker). */
function LarCcuCard({ initialPeriod }) {
  const [period, setPeriod] = React.useState(initialPeriod || "month");
  const { data: ccu, error, reload } = useLarLoad(() => api(`/api/stats/ccu?period=${period}`), [period]);

  React.useEffect(() => {
    const t = setInterval(() => reload(), 120000);
    return () => clearInterval(t);
  }, [period]);

  const peakDelta = () => {
    const c = ccu?.current?.peak, p = ccu?.previous?.peak;
    if (c == null || p == null || p === 0) return null;
    const d = ((c - p) / p) * 100;
    return <span style={{color: d >= 0 ? "var(--success)" : "var(--danger)", fontWeight:600}}>{fmt.pct(d)}</span>;
  };
  const points = ccu?.current?.points || [];

  return (
    <div className="card card-pad section-gap">
      <div className="card-head">
        <div>
          <div className="card-title"><span className="pulse" style={{display:"inline-block", verticalAlign:"middle", marginRight:8}}/>CCU{ccu?.current?.now != null ? <> · now <span style={{color:"var(--ink)"}}>{fmt.num(ccu.current.now)}</span> in-game</> : " · concurrent players"}</div>
          <div className="card-sub">
            {ccu ? <>{ccu.current.label}: peak <strong>{fmt.num(ccu.current.peak)}</strong>{ccu.previous?.peak != null && <> vs {ccu.previous.label}: {fmt.num(ccu.previous.peak)} {peakDelta()}</>}</> : "from Photon"}
          </div>
        </div>
        <div className="h-stack gap-6">
          {LAR_CCU_PERIODS.map(([id, lab]) => <button key={id} className="filter-pill" data-active={period === id} onClick={() => setPeriod(id)}>{lab}</button>)}
        </div>
      </div>
      {error && <div className="empty"><h3>Live CCU unavailable</h3><p>{error}</p></div>}
      {!error && points.length > 1 && (ccu.previous?.points?.length > 1
        ? <LarComparisonChart current={points.map(p => p.v)} previous={ccu.previous.points.map(p => p.v)} xLabels={points.map(p => larCcuTime(p.t, period))}
            curLabel={ccu.current.label} prevLabel={ccu.previous.label} labelFmt={fmt.short}/>
        : <AreaChart data={points.map(p => p.v)} xLabels={points.map(p => larCcuTime(p.t, period))} width={1020} height={220} accent="#10b981" labelFmt={fmt.short}/>)}
      {!error && ccu && points.length < 2 && <div className="empty"><h3>No CCU data</h3><p>{ccu.current?.unparsed ? "Photon's response was not recognised; check the Worker logs." : "No data points in this window."}</p></div>}
      {!error && !ccu && <div className="empty"><h3>Loading…</h3></div>}
    </div>
  );
}

function LarOverviewPage() {
  const dash = window.dashSettings();
  const canStats = can("stats.view");
  const stats = useLarLoad(() => (canStats ? larApi("stats") : Promise.resolve(null)), []);
  const [days, setDays] = React.useState(dash.dailyDays || 30);
  const daily = useLarLoad(() => (canStats && dash.showDaily ? larApi(`stats/daily?days=${days}`) : Promise.resolve(null)), [days]);
  const [metric, setMetric] = React.useState(dash.dailyMetric || "active");
  const activity = useLarLoad(() => (can("players.view") && dash.showActivity ? larApi("audit?limit=12") : Promise.resolve(null)), []);

  // Online changes by the minute; refresh the numbers while the page is open.
  React.useEffect(() => {
    if (!canStats) return;
    const t = setInterval(() => stats.reload(), 30000);
    return () => clearInterval(t);
  }, [canStats]);

  const s = stats.data;
  const series = daily.data?.days || [];
  const METRICS = [["active", "Daily active players", "#6366f1"], ["new", "New players", "#10b981"], ["migrated", "Migrated from PlayFab", "#f59e0b"]];
  const current = METRICS.find(m => m[0] === metric) || METRICS[0];

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1 className="page-title">Dashboard</h1>
          <p className="page-subtitle">Live numbers from LAR's own backend.</p>
        </div>
        <div className="page-head-actions"><button className="btn btn-sm" onClick={() => { stats.reload(); daily.reload(); activity.reload(); }}>Refresh</button></div>
      </div>

      {stats.error && <div className="card card-pad"><LarError error={stats.error}/></div>}

      {canStats && (
        <>
          <div className="kpi-grid">
            <LarKpi label="Total players" icon="users" value={s ? fmt.short(s.players) : null} sub="accounts on the LAR API"/>
            <LarKpi label="Online now" icon="globe" value={s ? fmt.num(s.online) : null} sub="connected to the live channel" tone={s?.online ? "success" : undefined}/>
            <LarKpi label="Active · 24h" icon="session" value={s ? fmt.num(s.active24h) : null} sub="logged in today"/>
            <LarKpi label="New · 24h" icon="plus" value={s ? fmt.num(s.new24h) : null} sub="brand-new accounts"/>
          </div>
          <div className="kpi-grid" style={{marginTop:12}}>
            <LarKpi label="Migrated from PlayFab" value={s ? fmt.num(s.migrated) : null}/>
            <LarKpi label="Copied, not logged in yet" value={s ? fmt.num(s.unclaimed) : null}/>
            <LarKpi label="Banned" value={s ? fmt.num(s.banned) : null} tone={s?.banned ? "danger" : undefined}/>
            <LarKpi label="Gold in circulation" icon="coin" value={s ? fmt.short(s.goldInCirculation) : null}/>
          </div>

          {dash.showCcu && window.APP_GAME_INFO?.hasPhoton && <LarCcuCard initialPeriod={dash.ccuPeriod}/>}

          {dash.showDaily && <div className="card card-pad section-gap">
            <div className="card-head">
              <div><div className="card-title">{current[1]}</div><div className="card-sub">per UTC day</div></div>
              <div className="h-stack gap-6 wrap">
                {METRICS.map(([id, lab]) => <button key={id} className="filter-pill" data-active={metric === id} onClick={() => setMetric(id)}>{lab}</button>)}
                <span style={{width:8}}/>
                {[7, 30, 90].map(d => <button key={d} className="filter-pill" data-active={days === d} onClick={() => setDays(d)}>{d}d</button>)}
              </div>
            </div>
            {series.length > 1
              ? <AreaChart data={series.map(d => d[current[0]])} xLabels={series.map(d => d.day.slice(5))} accent={current[2]} labelFmt={fmt.short}/>
              : <div className="empty"><h3>{daily.loading ? "Loading…" : "No data yet"}</h3><p>Logins through the LAR API show up here.</p></div>}
          </div>}
        </>
      )}

      <div className="row row-1-1 section-gap">
        {can("migration.run") && dash.showMigration && <LarMigrationCard compact/>}
        {can("players.view") && dash.showActivity && (
          <div className="card card-pad">
            <div className="card-head"><div><div className="card-title">Recent staff activity</div><div className="card-sub">everything done through the API</div></div></div>
            <div className="stack gap-6 mt-8">
              {(activity.data?.entries || []).map(e => (
                <div key={e.id} className="h-stack gap-8 tiny" style={{padding:"4px 0", borderBottom:"1px solid var(--border)"}}>
                  <span className="tag">{e.action}</span>
                  <span style={{flex:1, minWidth:0, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>{e.actor}{e.target ? <span className="muted mono"> → {e.target}</span> : null}</span>
                  <span className="muted">{timeSince(larIso(e.at))}</span>
                </div>
              ))}
              {activity.data && !activity.data.entries.length && <div className="empty"><h3>Quiet</h3><p>No staff actions yet.</p></div>}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { LarOverviewPage, LarMigrationCard });
