/* ==================================================================
   Admin → KinetiX Activity
   ------------------------------------------------------------------
   Two tabs over the learn-kinetiX chat tables (chat_sessions +
   chat_messages, shared Postgres).

     • Chats — list of sessions with filters; click → full transcript
     • Usage — token + cost rollup, per-student leaderboard, daily series

   Plus a "Set daily limit" affordance on each row of the leaderboard
   so an admin can lift / restrict a specific student.

   Endpoints (all admin-only, school-scoped on the backend):
     GET  /api/admin/kinetix/activity/chats
     GET  /api/admin/kinetix/activity/chats/:session_id
     GET  /api/admin/kinetix/activity/usage?days=30
     PUT  /api/admin/kinetix/access/:student_id/token-limit  { limit }
   ================================================================== */

const { Icon: KAct } = window.KXUI;

const KinetixActivityScreen = () => {
  const [tab, setTab] = React.useState("chats"); // 'chats' | 'usage'

  return (
    <div style={{ overflow: "auto", padding: "24px 28px", height: "100%" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }}>
        <span style={{ color: "var(--accent)" }}><KAct name="sparkle" size={18}/></span>
        <div className="muted" style={{ fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase" }}>Admin · Activity</div>
      </div>
      <h1 style={{ margin: "4px 0 6px", color: "var(--ink-0)", fontFamily: "'Instrument Serif', serif", fontWeight: 400, fontSize: 32, letterSpacing: "-0.01em" }}>
        KinetiX Activity
      </h1>
      <p className="muted" style={{ margin: 0, fontSize: 13.5, maxWidth: 720 }}>
        See what students are chatting about and how many tokens they're spending.
        Token data comes straight from Gemini's <span className="mono">usageMetadata</span>; cost
        estimates use the published per-model rates.
      </p>

      <div style={{ display: "flex", gap: 6, margin: "18px 0 14px", borderBottom: "1px solid var(--line-soft)" }}>
        <TabPill active={tab === "chats"} onClick={() => setTab("chats")}>Chats</TabPill>
        <TabPill active={tab === "usage"} onClick={() => setTab("usage")}>Usage</TabPill>
      </div>

      {tab === "chats" && <ChatsTab/>}
      {tab === "usage" && <UsageTab/>}
    </div>
  );
};

const TabPill = ({ active, children, onClick }) => (
  <button onClick={onClick}
    style={{
      background: "transparent", border: 0, padding: "8px 14px", cursor: "pointer",
      color: active ? "var(--ink-0)" : "var(--ink-3)",
      borderBottom: active ? "2px solid var(--accent)" : "2px solid transparent",
      fontSize: 13, fontWeight: active ? 600 : 400,
      marginBottom: -1,
    }}>{children}</button>
);

/* ====================================================================
   Tab 1 — Chats
   ==================================================================== */

const ChatsTab = () => {
  const [filters, setFilters] = React.useState({
    student_id: "", class_section_id: "", subject_id: "",
    date_from: "", date_to: "",
  });
  const [sessions, setSessions] = React.useState([]);
  const [loading, setLoading]   = React.useState(true);
  const [err, setErr]           = React.useState(null);
  const [selected, setSelected] = React.useState(null); // session_id
  const [classSections, setClassSections] = React.useState([]);
  const [subjects, setSubjects]           = React.useState([]);

  const load = React.useCallback(async () => {
    setLoading(true); setErr(null);
    try {
      const qs = new URLSearchParams();
      Object.entries(filters).forEach(([k, v]) => { if (v) qs.set(k, v); });
      const r = await window.KXApi.get(`/admin/kinetix/activity/chats?${qs.toString()}`);
      setSessions(r.sessions);
    } catch (e) {
      setErr(String(e.message || e));
    } finally {
      setLoading(false);
    }
  }, [filters]);

  React.useEffect(() => { load(); }, [load]);
  React.useEffect(() => {
    window.KXApi.get("/admin/class-sections").then(setClassSections).catch(() => {});
    window.KXApi.get("/catalog/subjects").then(setSubjects).catch(() => {});
  }, []);

  const onFilter = (k, v) => setFilters((s) => ({ ...s, [k]: v }));

  return (
    <div>
      <div className="card" style={{ marginBottom: 12 }}>
        <div className="card-body" style={{ padding: 14, display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
          <FilterField label="Class">
            <select className="input" value={filters.class_section_id} onChange={(e) => onFilter("class_section_id", e.target.value)}
              style={{ padding: "4px 8px", fontSize: 12 }}>
              <option value="">All</option>
              {classSections.map(cs => <option key={cs.id} value={cs.id}>{cs.label}</option>)}
            </select>
          </FilterField>
          <FilterField label="Subject">
            <select className="input" value={filters.subject_id} onChange={(e) => onFilter("subject_id", e.target.value)}
              style={{ padding: "4px 8px", fontSize: 12 }}>
              <option value="">All</option>
              {subjects.map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
            </select>
          </FilterField>
          <FilterField label="From">
            <input type="date" className="input" value={filters.date_from} onChange={(e) => onFilter("date_from", e.target.value)}
              style={{ padding: "4px 6px", fontSize: 12 }}/>
          </FilterField>
          <FilterField label="To">
            <input type="date" className="input" value={filters.date_to} onChange={(e) => onFilter("date_to", e.target.value)}
              style={{ padding: "4px 6px", fontSize: 12 }}/>
          </FilterField>
          <button className="btn ghost sm" onClick={() => setFilters({ student_id: "", class_section_id: "", subject_id: "", date_from: "", date_to: "" })}
            style={{ marginLeft: "auto" }}>Reset</button>
        </div>
      </div>

      <div className="card">
        <div className="card-head"><span className="card-title">{sessions.length} session{sessions.length === 1 ? "" : "s"}</span></div>
        <div className="card-body" style={{ padding: 0 }}>
          {loading && <div style={{ padding: 20, color: "var(--ink-3)", fontSize: 12 }}>Loading…</div>}
          {err && <div style={{ padding: 16, color: "var(--red)", fontSize: 12 }}>{err}</div>}
          {!loading && sessions.length === 0 && (
            <div style={{ padding: 32, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
              No chat sessions match.
            </div>
          )}
          {sessions.map(s => (
            <div key={s.id}
              onClick={() => setSelected(s.id)}
              className="cx-actrow"
              style={{
                padding: "10px 14px", borderBottom: "1px solid var(--line-soft)",
                gap: 10, alignItems: "center", cursor: "pointer", fontSize: 12,
              }}>
              <span className="mono muted">{s.roll_no}</span>
              <div>
                <div style={{ color: "var(--ink-0)", fontWeight: 500 }}>{s.student_name}</div>
                <div className="muted" style={{ fontSize: 10.5, marginTop: 1 }}>
                  {s.subject_name || "—"} · {s.test_title || "—"}
                </div>
              </div>
              <span className="muted">{s.class_label || "—"}</span>
              <span className="mono muted">{new Date(s.updated_at).toLocaleString()}</span>
              <span style={{ color: "var(--ink-2)" }}>{s.message_count} msgs</span>
              <span className="mono" style={{ color: "var(--accent)" }}>
                {fmtTokens(s.prompt_tokens + s.output_tokens)}
              </span>
              <span className="mono muted">{fmtUsd(s.est_cost_usd)}</span>
            </div>
          ))}
        </div>
      </div>

      {selected && (
        <SessionDrawer
          sessionId={selected}
          onClose={() => setSelected(null)}
        />
      )}
    </div>
  );
};

const FilterField = ({ label, children }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
    <span className="muted" style={{ fontSize: 9.5, letterSpacing: ".12em", textTransform: "uppercase" }}>{label}</span>
    {children}
  </div>
);

const SessionDrawer = ({ sessionId, onClose }) => {
  const [data, setData] = React.useState(null);
  const [err, setErr]   = React.useState(null);

  React.useEffect(() => {
    let cancel = false;
    window.KXApi.get(`/admin/kinetix/activity/chats/${sessionId}`)
      .then((r) => { if (!cancel) setData(r); })
      .catch((e) => { if (!cancel) setErr(String(e.message || e)); });
    return () => { cancel = true; };
  }, [sessionId]);

  return (
    <div onClick={onClose}
      style={{
        position: "fixed", inset: 0, background: "rgba(0,0,0,0.45)",
        zIndex: 50, display: "flex", justifyContent: "flex-end",
      }}>
      <div onClick={(e) => e.stopPropagation()}
        style={{
          width: "min(640px, 95vw)", background: "var(--bg-0)", height: "100%",
          borderLeft: "1px solid var(--line)", overflow: "auto", padding: 20,
        }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
          <div className="muted" style={{ fontSize: 10, letterSpacing: ".16em", textTransform: "uppercase" }}>Chat session</div>
          <button className="btn ghost sm" onClick={onClose}><KAct name="x" size={12}/></button>
        </div>
        {err && <div style={{ color: "var(--red)", fontSize: 12 }}>{err}</div>}
        {!data && !err && <div style={{ color: "var(--ink-3)", fontSize: 12 }}>Loading…</div>}
        {data && (
          <>
            <h2 style={{ margin: "0 0 4px", fontSize: 18, fontWeight: 600 }}>{data.session.student_name}</h2>
            <div className="muted" style={{ fontSize: 12 }}>
              {data.session.roll_no} · {data.session.class_label || "—"} ·{" "}
              {data.session.subject_name || "—"} · {data.session.test_title}
            </div>

            <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 14 }}>
              <Stat label="Messages" value={data.session.message_count}/>
              <Stat label="Input tok" value={fmtTokens(data.session.prompt_tokens)}/>
              <Stat label="Output tok" value={fmtTokens(data.session.output_tokens)}/>
              <Stat label="Est. cost" value={fmtUsd(data.session.est_cost_usd)}/>
            </div>

            {data.session.question_text && (
              <div style={{ marginTop: 14, padding: 10, background: "var(--bg-2)", borderRadius: 6, fontSize: 12 }}>
                <div style={{ fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-3)", marginBottom: 4 }}>Question</div>
                <RenderBody body={data.session.question_text}/>
              </div>
            )}

            <div style={{ marginTop: 18 }}>
              {data.messages.map((m) => (
                <MessageBubble key={m.id} m={m}/>
              ))}
              {data.messages.length === 0 && (
                <div style={{ color: "var(--ink-3)", fontSize: 12, padding: 14, textAlign: "center" }}>
                  No messages yet.
                </div>
              )}
            </div>
          </>
        )}
      </div>
    </div>
  );
};

const MessageBubble = ({ m }) => {
  const isUser = m.role === "user";
  const isSystem = m.role === "system";
  return (
    <div style={{
      marginBottom: 8, padding: "8px 10px", borderRadius: 6,
      background: isUser ? "rgba(46, 91, 255, 0.06)"
                : isSystem ? "rgba(120,120,120,0.05)"
                : "var(--bg-2)",
      border: "1px solid var(--line-soft)",
      fontSize: 12, lineHeight: 1.55, whiteSpace: "pre-wrap",
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
        <span className="mono" style={{ fontSize: 9.5, letterSpacing: ".14em", textTransform: "uppercase",
                                          color: isUser ? "var(--accent)" : isSystem ? "var(--ink-3)" : "var(--ink-2)" }}>
          {m.role} · {m.mode}{m.error ? " · error" : ""}
        </span>
        <span className="mono muted" style={{ fontSize: 9.5 }}>
          {m.prompt_tokens || m.output_tokens
            ? `${m.prompt_tokens || 0} → ${m.output_tokens || 0} tok`
            : ""}
          {m.latency_ms ? ` · ${m.latency_ms}ms` : ""}
        </span>
      </div>
      <RenderBody body={m.body}/>
      {m.error && (
        <div style={{ marginTop: 4, fontSize: 10.5, color: "var(--red)" }}>{m.error}</div>
      )}
    </div>
  );
};

/* Pretty-prints a chat message body. When the body parses as a JSON object,
   each top-level field is shown as a labeled block (strings keep their \n
   linebreaks; nested objects/arrays render as indented JSON). Falls back to
   plain text when the body isn't JSON. */
const RenderBody = ({ body }) => {
  const parsed = React.useMemo(() => {
    if (typeof body !== "string") return null;
    const s = body.trim();
    if (!s.startsWith("{") && !s.startsWith("[")) return null;
    try { return JSON.parse(s); } catch { return null; }
  }, [body]);

  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
    const entries = Object.entries(parsed).filter(
      ([, v]) => !(v === null || v === undefined || v === "")
    );
    if (entries.length === 0) {
      return <div className="muted" style={{ fontSize: 11 }}>(empty)</div>;
    }
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 10, color: "var(--ink-0)" }}>
        {entries.map(([k, v]) => <JsonField key={k} label={k} value={v}/>)}
      </div>
    );
  }
  if (Array.isArray(parsed)) {
    return (
      <pre className="mono" style={{ margin: 0, padding: 8, background: "var(--bg-2)", borderRadius: 4, fontSize: 11, color: "var(--ink-1)", overflowX: "auto", whiteSpace: "pre-wrap" }}>
        {JSON.stringify(parsed, null, 2)}
      </pre>
    );
  }
  return <div style={{ color: "var(--ink-0)", whiteSpace: "pre-wrap" }}>{body}</div>;
};

const JsonField = ({ label, value }) => {
  const isString = typeof value === "string";
  const isObj = value !== null && typeof value === "object";
  return (
    <div>
      <div className="mono" style={{ fontSize: 9.5, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--ink-3)", marginBottom: 3 }}>
        {label}
      </div>
      {isString && (
        <div style={{ color: "var(--ink-0)", whiteSpace: "pre-wrap", lineHeight: 1.55 }}>{value}</div>
      )}
      {!isString && !isObj && (
        <div className="mono" style={{ color: "var(--ink-0)" }}>{String(value)}</div>
      )}
      {isObj && (
        <pre className="mono" style={{ margin: 0, padding: 8, background: "var(--bg-2)", borderRadius: 4, fontSize: 11, color: "var(--ink-1)", overflowX: "auto", whiteSpace: "pre-wrap" }}>
          {JSON.stringify(value, null, 2)}
        </pre>
      )}
    </div>
  );
};

const Stat = ({ label, value }) => (
  <div style={{ background: "var(--bg-2)", padding: "8px 10px", borderRadius: 6 }}>
    <div className="muted" style={{ fontSize: 9.5, letterSpacing: ".12em", textTransform: "uppercase" }}>{label}</div>
    <div className="mono" style={{ fontSize: 16, color: "var(--ink-0)", marginTop: 2 }}>{value}</div>
  </div>
);

/* ====================================================================
   Tab 2 — Usage
   ==================================================================== */

const UsageTab = () => {
  const [days, setDays]   = React.useState(30);
  const [data, setData]   = React.useState(null);
  const [err,  setErr]    = React.useState(null);
  const [editingStudent, setEditingStudent] = React.useState(null); // { student_id, name, current_limit }

  const load = React.useCallback(async () => {
    setErr(null);
    try {
      setData(await window.KXApi.get(`/admin/kinetix/activity/usage?days=${days}`));
    } catch (e) {
      setErr(String(e.message || e));
    }
  }, [days]);

  React.useEffect(() => { load(); }, [load]);

  if (err) return <div style={{ color: "var(--red)", padding: 16 }}>{err}</div>;
  if (!data) return <div style={{ color: "var(--ink-3)", padding: 24, textAlign: "center" }}>Loading…</div>;

  const t = data.totals;
  const defaultLimit = data.default_daily_token_limit;

  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
        <span className="muted" style={{ fontSize: 11, letterSpacing: ".12em", textTransform: "uppercase" }}>Window</span>
        {[7, 30, 90].map((d) => (
          <button key={d} onClick={() => setDays(d)}
            className={"btn sm " + (days === d ? "primary" : "ghost")}>{d}d</button>
        ))}
        <span className="muted" style={{ marginLeft: "auto", fontSize: 11 }}>
          Default daily cap: <span className="mono">{fmtTokens(defaultLimit)}</span>
        </span>
      </div>

      {/* Top-line totals */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 10, marginBottom: 18 }}>
        <BigStat label={`Messages (${days}d)`} value={t.messages_window} sub={`${t.messages_total} all-time`}/>
        <BigStat label={`Input tokens (${days}d)`} value={fmtTokens(t.prompt_tokens_window)} sub={`${fmtTokens(t.prompt_tokens_total)} all-time`}/>
        <BigStat label={`Output tokens (${days}d)`} value={fmtTokens(t.output_tokens_window)} sub={`${fmtTokens(t.output_tokens_total)} all-time`}/>
        <BigStat label={`Est. cost (${days}d)`} value={fmtUsd(t.est_cost_usd_window)} sub={`${fmtUsd(t.est_cost_usd_total)} all-time`}/>
      </div>

      {/* Sparkline */}
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-head"><span className="card-title">Daily tokens — last {days} days</span></div>
        <div className="card-body" style={{ padding: 14 }}>
          <Sparkline series={data.series}/>
        </div>
      </div>

      {/* Per-mode split */}
      {data.per_mode.length > 0 && (
        <div className="card" style={{ marginBottom: 14 }}>
          <div className="card-head"><span className="card-title">By mode (window)</span></div>
          <div className="card-body" style={{ padding: 14, display: "flex", gap: 14, flexWrap: "wrap" }}>
            {data.per_mode.map(pm => (
              <div key={pm.mode} style={{ padding: "8px 12px", background: "var(--bg-2)", borderRadius: 6, minWidth: 120 }}>
                <div className="muted" style={{ fontSize: 9.5, letterSpacing: ".12em", textTransform: "uppercase" }}>{pm.mode}</div>
                <div className="mono" style={{ fontSize: 14, color: "var(--ink-0)", marginTop: 2 }}>
                  {fmtTokens(pm.in_t + pm.out_t)} tok · {pm.messages} msgs
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Per-student leaderboard */}
      <div className="card">
        <div className="card-head">
          <span className="card-title">Per-student usage ({days}d)</span>
          <span className="muted" style={{ marginLeft: "auto", fontSize: 11 }}>Sorted by total tokens, top 200</span>
        </div>
        <div className="card-body" style={{ padding: 0 }}>
          {data.per_student.length === 0 && (
            <div style={{ padding: 32, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
              No usage in this window.
            </div>
          )}
          {data.per_student.map((u) => {
            const effectiveLimit = u.daily_limit_override ?? defaultLimit;
            const total = u.in_t + u.out_t;
            return (
              <div className="cx-actrow wide" key={u.student_id}
                style={{
                  padding: "10px 14px", borderBottom: "1px solid var(--line-soft)",
                  
                  gap: 10, alignItems: "center", fontSize: 12,
                }}>
                <span className="mono muted">{u.roll_no}</span>
                <div>
                  <div style={{ color: "var(--ink-0)", fontWeight: 500 }}>{u.name}</div>
                  <div className="muted" style={{ fontSize: 10.5, marginTop: 1 }}>{u.class_label || "—"}</div>
                </div>
                <span style={{ color: "var(--ink-2)" }}>{u.sessions} sess · {u.messages} msgs</span>
                <span className="mono" style={{ color: "var(--accent)" }}>{fmtTokens(total)} tok</span>
                <span className="mono muted">{fmtUsd(u.est_cost_usd)}</span>
                <span className="mono"
                      style={{ color: u.daily_limit_override != null ? "var(--accent)" : "var(--ink-3)" }}>
                  {u.daily_limit_override != null
                    ? `${fmtTokens(u.daily_limit_override)} cap`
                    : `default`}
                </span>
                <button className="btn ghost sm"
                  onClick={() => setEditingStudent({
                    student_id: u.student_id, name: u.name,
                    current_limit: u.daily_limit_override,
                  })}>
                  Set limit
                </button>
              </div>
            );
          })}
        </div>
      </div>

      {editingStudent && (
        <SetLimitDialog
          studentId={editingStudent.student_id}
          name={editingStudent.name}
          currentLimit={editingStudent.current_limit}
          defaultLimit={defaultLimit}
          onClose={() => setEditingStudent(null)}
          onSaved={async () => { setEditingStudent(null); await load(); }}
        />
      )}
    </div>
  );
};

const BigStat = ({ label, value, sub }) => (
  <div style={{ background: "var(--bg-2)", padding: "12px 14px", borderRadius: 8 }}>
    <div className="muted" style={{ fontSize: 10, letterSpacing: ".14em", textTransform: "uppercase" }}>{label}</div>
    <div className="mono" style={{ fontSize: 20, color: "var(--ink-0)", marginTop: 4 }}>{value}</div>
    {sub && <div className="muted" style={{ fontSize: 10, marginTop: 2 }}>{sub}</div>}
  </div>
);

const Sparkline = ({ series }) => {
  if (!series || series.length === 0) return <div className="muted" style={{ fontSize: 12 }}>No data.</div>;
  const W = 720, H = 80, PAD = 4;
  const max = Math.max(1, ...series.map(p => p.in_t + p.out_t));
  const step = (W - PAD * 2) / Math.max(1, series.length - 1);
  const pts = series.map((p, i) => {
    const v = p.in_t + p.out_t;
    const x = PAD + i * step;
    const y = H - PAD - ((v / max) * (H - PAD * 2));
    return [x, y, p];
  });
  const d = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"} ${x.toFixed(1)} ${y.toFixed(1)}`).join(" ");
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: H, display: "block" }} preserveAspectRatio="none">
      <path d={d} fill="none" stroke="var(--accent)" strokeWidth="1.5"/>
      <path d={`${d} L ${pts[pts.length-1][0]} ${H} L ${pts[0][0]} ${H} Z`} fill="var(--accent)" opacity="0.08"/>
      {pts.map(([x, y, p], i) => (
        <circle key={i} cx={x} cy={y} r="1.4" fill="var(--accent)">
          <title>{`${p.d}: ${fmtTokens(p.in_t + p.out_t)} tokens, ${p.msgs} msgs`}</title>
        </circle>
      ))}
    </svg>
  );
};

const SetLimitDialog = ({ studentId, name, currentLimit, defaultLimit, onClose, onSaved }) => {
  const [val, setVal] = React.useState(currentLimit != null ? String(currentLimit) : "");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr]   = React.useState(null);

  const save = async (limit) => {
    setBusy(true); setErr(null);
    try {
      await window.KXApi.put(`/admin/kinetix/access/${studentId}/token-limit`, { limit });
      await onSaved();
    } catch (e) {
      setErr(String(e.message || e));
    } finally {
      setBusy(false);
    }
  };

  return (
    <div onClick={onClose}
      style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.45)", zIndex: 50, display: "grid", placeItems: "center", padding: 16 }}>
      <div onClick={(e) => e.stopPropagation()}
        style={{ background: "var(--bg-0)", border: "1px solid var(--line)", borderRadius: 8, padding: 18, width: "min(440px, 95vw)" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>Daily token limit</div>
          <button className="btn ghost sm" onClick={onClose}><KAct name="x" size={12}/></button>
        </div>
        <p className="muted" style={{ fontSize: 12, margin: "0 0 12px" }}>
          For <b>{name}</b>. Default for the school is <span className="mono">{fmtTokens(defaultLimit)}</span> tokens/day.
          Set a number to override; clear to fall back to the default.
        </p>
        <input type="number" min="0" step="1000" className="input"
          value={val} onChange={(e) => setVal(e.target.value)}
          placeholder={`Leave blank to inherit (${defaultLimit})`}
          style={{ width: "100%", padding: "8px 10px", fontSize: 13 }}/>
        {err && <div style={{ color: "var(--red)", fontSize: 11, marginTop: 6 }}>{err}</div>}
        <div style={{ display: "flex", gap: 8, marginTop: 14, justifyContent: "flex-end" }}>
          <button className="btn ghost sm" onClick={() => save(null)} disabled={busy}>
            Clear override
          </button>
          <button className="btn sm primary"
            disabled={busy || (val !== "" && !Number.isFinite(parseInt(val, 10)))}
            onClick={() => save(val === "" ? null : parseInt(val, 10))}>
            {busy ? "Saving…" : "Save"}
          </button>
        </div>
      </div>
    </div>
  );
};

/* ====================================================================
   Helpers
   ==================================================================== */
function fmtTokens(n) {
  n = Number(n) || 0;
  if (n < 1000) return String(n);
  if (n < 1_000_000) return (n / 1000).toFixed(n < 10_000 ? 1 : 0) + "k";
  return (n / 1_000_000).toFixed(2) + "M";
}
function fmtUsd(n) {
  n = Number(n) || 0;
  if (n === 0) return "$0";
  if (n < 0.01) return "<$0.01";
  if (n < 1)    return "$" + n.toFixed(3);
  return "$" + n.toFixed(2);
}

window.KinetixActivityScreen = KinetixActivityScreen;
