// Models — the comprehensive model-management plane that mirrors the Hermes Agent
// dashboard's Models page: a Main Model selector, the 11 Auxiliary Task overrides
// (each independently pinnable, with "Reset all to auto"), and per-model usage
// analytics over 7/30/90 days. Wired to the dashboard config plane via /dash/*
// (HERMES.modelOptions / modelAuxiliary / setModel / resetAuxModels / analyticsModels),
// with an offline fallback to window.DATA so the standalone preview stays rendered.
const { useState, useEffect, useCallback, useMemo } = React;

// The 11 background tasks the agent farms out to auxiliary models (order = backend
// _AUX_TASK_SLOTS). Labels/descriptions/icons are display-only.
const AUX_TASKS = [
  { task: "vision",            label: "Vision",            icon: "file",       desc: "Image understanding & OCR for screenshots and attachments." },
  { task: "web_extract",       label: "Web Extract",       icon: "external",   desc: "Reads and distills fetched web pages into clean text." },
  { task: "compression",       label: "Compression",       icon: "gauge",      desc: "Summarizes long context to keep it inside the window." },
  { task: "skills_hub",        label: "Skills Hub",        icon: "skills",     desc: "Searches and selects skills from the hub." },
  { task: "approval",          label: "Approval",          icon: "lock",       desc: "Evaluates tool-call approval decisions." },
  { task: "mcp",               label: "MCP",               icon: "mcp",        desc: "Interprets and routes MCP tool calls." },
  { task: "title_generation",  label: "Title Generation",  icon: "edit",       desc: "Names new sessions automatically." },
  { task: "triage_specifier",  label: "Triage Specifier",  icon: "activity",   desc: "Classifies and routes incoming requests." },
  { task: "kanban_decomposer", label: "Kanban Decomposer", icon: "kanban",     desc: "Breaks larger tasks into kanban sub-tasks." },
  { task: "profile_describer", label: "Profile Describer", icon: "profiles",   desc: "Writes agent profile descriptions." },
  { task: "curator",           label: "Curator",           icon: "memory",     desc: "Maintains memory and prunes stale skills." },
];
const AUX_LABEL = AUX_TASKS.reduce((o, t) => { o[t.task] = t.label; return o; }, {});

const WINDOWS = [["7", "7D"], ["30", "30D"], ["90", "90D"]];

// ---- formatting helpers ----
function fmtTokens(n) {
  n = Number(n) || 0;
  if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
  if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
  if (n >= 1e3) return (n / 1e3).toFixed(1) + "K";
  return String(n);
}
function fmtCost(n) {
  n = Number(n) || 0;
  if (n === 0) return "$0";
  if (n < 0.01) return "$" + n.toFixed(4);
  if (n < 1) return "$" + n.toFixed(3);
  return "$" + n.toFixed(2);
}
function fmtAgo(epochSec) {
  if (!epochSec) return "—";
  const s = Math.max(0, Date.now() / 1000 - Number(epochSec));
  if (s < 60) return "just now";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  if (s < 86400) return Math.floor(s / 3600) + "h ago";
  return Math.floor(s / 86400) + "d ago";
}
function fmtCtx(n) {
  n = Number(n) || 0;
  if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e6 && n % 1e6 ? 1 : 0) + "M";
  if (n >= 1e3) return Math.round(n / 1e3) + "K";
  return n ? String(n) : "—";
}
// "openrouter/openai/gpt-5.5" / "openai/gpt-4o" -> short display
function shortModel(id) {
  if (!id) return "";
  const parts = String(id).split("/");
  return parts[parts.length - 1];
}
function assignLabel(provider, model) {
  const p = (provider || "").toLowerCase();
  if (!model && (!p || p === "auto")) return "Auto";
  return model || (p ? p : "Auto");
}

// =====================================================================
// Model picker modal — lists authenticated providers + their models from
// /api/model/options. `allowAuto` adds an "Auto" row that clears the pin.
// onPick({ provider, model }). Pricing (if present) is shown inline.
// =====================================================================
function ModelPicker({ open, title, sub, options, current, allowAuto, busy, onPick, onClose }) {
  const [filter, setFilter] = useState("");
  useEffect(() => { if (open) setFilter(""); }, [open]);
  if (!open) return null;

  const providers = (options && options.providers ? options.providers : [])
    .filter((p) => (p.models && p.models.length) || p.is_current)
    .map((p) => ({
      ...p,
      models: (p.models || []).filter((m) => !filter || m.toLowerCase().includes(filter.toLowerCase()) || (p.name || "").toLowerCase().includes(filter.toLowerCase())),
    }))
    .filter((p) => p.models.length);

  return (
    <div className="modal-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" role="dialog" aria-modal="true">
        <div className="modal-head">
          <div className="ic"><Icon name="cpu" size={20} /></div>
          <div>
            <h2>{title}</h2>
            <p>{sub}</p>
          </div>
          <button className="icon-btn x" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body">
          <div className="field">
            <div className="search" style={{ width: "100%" }}>
              <Icon name="search" size={15} />
              <input autoFocus placeholder="Filter models or providers…" value={filter} onChange={(e) => setFilter(e.target.value)} />
            </div>
          </div>

          {allowAuto && (
            <button className="row" disabled={busy} onClick={() => onPick({ provider: "auto", model: "" })} style={{ textAlign: "left" }}>
              <span className="glyph"><Icon name="sparkle" size={15} /></span>
              <div className="row-main">
                <b>Auto</b>
                <div className="row-sub mono"><span>Let Hermes pick the best model for this task</span></div>
              </div>
              {(!current || !current.model) && <span className="badge pri"><span className="d" />current</span>}
            </button>
          )}

          {!providers.length && <div className="empty">No matching models.</div>}

          {providers.map((p) => (
            <div key={p.slug} style={{ marginBottom: 8 }}>
              <div className="flex between" style={{ margin: "10px 2px 6px" }}>
                <span className="section-title">{p.name || p.slug}</span>
                <span className="faint mono" style={{ fontSize: 11.5 }}>
                  {p.authenticated === false ? "needs setup" : `${p.models.length} model${p.models.length === 1 ? "" : "s"}`}
                </span>
              </div>
              <div className="rows">
                {p.models.map((m) => {
                  const isCur = current && current.provider && current.model &&
                    current.provider.toLowerCase() === p.slug.toLowerCase() && current.model === m;
                  const price = p.pricing && p.pricing[m];
                  return (
                    <button key={p.slug + "/" + m} className={cls("row", isCur && "sel")} disabled={busy}
                      onClick={() => onPick({ provider: p.slug, model: m })} style={{ textAlign: "left" }}>
                      <span className="glyph"><Icon name="cpu" size={15} /></span>
                      <div className="row-main">
                        <b className="mono" style={{ fontSize: 13 }}>{shortModel(m)}</b>
                        <div className="row-sub mono"><span>{m}</span></div>
                      </div>
                      {price && <span className="tag" title="input / output per 1M tokens">{price.input || price}{price.output ? ` · ${price.output}` : ""}</span>}
                      {isCur && <span className="badge pri"><span className="d" />current</span>}
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
        <div className="modal-foot">
          <button className="btn btn-ghost" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

// Expensive-model confirmation (set returns confirm_required:true).
function ConfirmExpensive({ data, busy, onConfirm, onCancel }) {
  if (!data) return null;
  return (
    <div className="modal-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel(); }}>
      <div className="modal sm" role="dialog" aria-modal="true">
        <div className="modal-head">
          <div className="ic" style={{ background: "var(--warn-soft)", color: "var(--warn)" }}><Icon name="bolt" size={20} /></div>
          <div>
            <h2>Confirm model</h2>
            <p className="mono">{data.model}</p>
          </div>
        </div>
        <div className="modal-body">
          <div style={{ padding: "12px 14px", border: "1px solid var(--line)", borderRadius: 10, background: "var(--surface-2)", fontSize: 13, lineHeight: 1.6, color: "var(--muted)" }}>
            {data.confirm_message || "This model may be expensive. Continue?"}
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn btn-ghost" onClick={onCancel} disabled={busy}>Cancel</button>
          <button className="btn btn-primary" onClick={onConfirm} disabled={busy}>{busy ? "Saving…" : "Use anyway"}</button>
        </div>
      </div>
    </div>
  );
}

function PageModels({ toast }) {
  const [main, setMain] = useState(null);        // {provider, model}
  const [aux, setAux] = useState([]);            // [{task, provider, model, base_url}]
  const [options, setOptions] = useState(null);  // model options for the picker
  const [live, setLive] = useState(null);        // null=loading, true=backend, false=offline
  const [picker, setPicker] = useState(null);    // {scope, task, current}
  const [confirm, setConfirm] = useState(null);  // pending expensive-model {body, message}
  const [busy, setBusy] = useState(false);
  const [days, setDays] = useState("30");
  const [analytics, setAnalytics] = useState(null); // {models, totals}
  const [anLoading, setAnLoading] = useState(false);

  const loadModels = useCallback(async () => {
    let anyLive = false;
    try {
      const a = await HERMES.modelAuxiliary();
      setMain(a.main || null);
      setAux(Array.isArray(a.tasks) ? a.tasks : []);
      anyLive = true;
    } catch {
      // offline fallback: synthesize from design data
      setMain({ provider: "auto", model: (window.DATA.models || [])[0] || "deepseek/deepseek-v4-flash" });
      setAux(AUX_TASKS.map((t) => ({ task: t.task, provider: "auto", model: "" })));
    }
    try { setOptions(await HERMES.modelOptions()); anyLive = true; } catch { setOptions(null); }
    setLive(anyLive);
  }, []);

  const loadAnalytics = useCallback(async (d) => {
    setAnLoading(true);
    try { setAnalytics(await HERMES.analyticsModels(Number(d))); }
    catch { setAnalytics(null); }
    setAnLoading(false);
  }, []);

  useEffect(() => { loadModels(); }, [loadModels]);
  useEffect(() => { loadAnalytics(days); }, [days, loadAnalytics]);

  // Apply a picker selection. Handles the expensive-model confirm round-trip.
  const apply = useCallback(async (sel, { scope, task }, confirmExpensive = false) => {
    const body = scope === "main"
      ? { scope: "main", provider: sel.provider, model: sel.model, confirm_expensive_model: confirmExpensive }
      : { scope: "auxiliary", task, provider: sel.provider || "auto", model: sel.model || "" };
    setBusy(true);
    try {
      const r = await HERMES.setModel(body);
      if (r && r.confirm_required) {
        setConfirm({ body, sel, scope, task, model: body.model, confirm_message: r.confirm_message });
        setBusy(false);
        return;
      }
      if (r && r.ok === false) { toast(r.detail || r.error || "Could not set model"); setBusy(false); return; }
      toast(scope === "main" ? `Main model → ${shortModel(sel.model) || "auto"}` : `${AUX_LABEL[task] || task} → ${assignLabel(sel.provider, sel.model)}`);
      setPicker(null); setConfirm(null);
      await loadModels();
    } catch (e) { toast(e.message || "Could not set model"); }
    setBusy(false);
  }, [loadModels, toast]);

  const confirmExpensive = useCallback(async () => {
    if (!confirm) return;
    setBusy(true);
    try {
      const r = await HERMES.setModel({ ...confirm.body, confirm_expensive_model: true });
      if (r && r.ok === false && !r.confirm_required) { toast(r.detail || "Could not set model"); setBusy(false); return; }
      toast(`Main model → ${shortModel(confirm.model)}`);
      setPicker(null); setConfirm(null);
      await loadModels();
    } catch (e) { toast(e.message || "Could not set model"); }
    setBusy(false);
  }, [confirm, loadModels, toast]);

  const resetAux = useCallback(async () => {
    setBusy(true);
    try { await HERMES.resetAuxModels(); toast("All auxiliary tasks reset to Auto"); await loadModels(); }
    catch (e) { toast(e.message || "Reset failed"); }
    setBusy(false);
  }, [loadModels, toast]);

  const mainProvider = (main && main.provider) || "auto";
  const mainModel = (main && main.model) || "—";
  const pinnedCount = aux.filter((a) => a.model || (a.provider && a.provider.toLowerCase() !== "auto")).length;

  const anModels = (analytics && analytics.models) || [];
  const totals = (analytics && analytics.totals) || null;

  return (
    <div className="page">
      <PageHead eyebrow="Workspace" title="Models" sub="The main model, per-task auxiliary overrides, and usage analytics for Hermes Agent.">
        {live === true && <span className="badge ok"><span className="d" />live</span>}
        {live === false && <span className="badge warn"><span className="d" />preview data</span>}
        {live === null && <span className="badge"><span className="d" />loading…</span>}
        <button className="btn btn-ghost" onClick={loadModels}><Icon name="refresh" size={16} />Refresh</button>
      </PageHead>

      {/* ---- Main model ---- */}
      <div className="card" style={{ marginBottom: "var(--gap)" }}>
        <div className="card-head">
          <div>
            <h2>Main model</h2>
            <p>The primary model the agent reasons and replies with. Applies to new sessions.</p>
          </div>
        </div>
        <div className="row" style={{ cursor: "default", alignItems: "center" }}>
          <span className="glyph" style={{ background: "var(--primary-soft)", color: "var(--primary)" }}><Icon name="bolt" size={16} /></span>
          <div className="row-main">
            <b className="mono" style={{ fontSize: 14.5 }}>{mainModel}</b>
            <div className="row-sub mono"><span>provider: {mainProvider}</span></div>
          </div>
          <button className="btn btn-primary btn-sm" disabled={busy || !options} onClick={() => setPicker({ scope: "main", current: { provider: mainProvider, model: main && main.model } })}>
            <Icon name="sliders" size={14} />Change
          </button>
        </div>
      </div>

      {/* ---- Auxiliary tasks ---- */}
      <div className="card" style={{ marginBottom: "var(--gap)" }}>
        <div className="card-head">
          <div>
            <h2>Auxiliary tasks</h2>
            <p>Background jobs each route to their own model. <b>Auto</b> lets Hermes choose; pin one to override.</p>
          </div>
          <div className="flex" style={{ gap: 8, alignItems: "center" }}>
            <span className="faint mono" style={{ fontSize: 12 }}>{pinnedCount} pinned · {aux.length} tasks</span>
            <button className="btn btn-ghost btn-sm" disabled={busy || !pinnedCount} onClick={resetAux}>
              <Icon name="refresh" size={14} />Reset all to auto
            </button>
          </div>
        </div>
        <div className="rows">
          {AUX_TASKS.map((meta) => {
            const cur = aux.find((a) => a.task === meta.task) || { provider: "auto", model: "" };
            const pinned = !!(cur.model || (cur.provider && cur.provider.toLowerCase() !== "auto"));
            return (
              <div className="row" key={meta.task} style={{ cursor: "default" }}>
                <span className="glyph"><Icon name={meta.icon} size={15} /></span>
                <div className="row-main">
                  <b style={{ fontSize: 14 }}>{meta.label}</b>
                  <div className="row-sub"><span className="faint">{meta.desc}</span></div>
                </div>
                {pinned
                  ? <span className="tag" style={{ color: "var(--primary)", borderColor: "var(--primary-ring)" }}>{shortModel(cur.model) || cur.provider}</span>
                  : <span className="tag">Auto</span>}
                <button className="btn btn-ghost btn-sm" disabled={busy || !options} onClick={() => setPicker({ scope: "auxiliary", task: meta.task, current: cur })}>
                  <Icon name="sliders" size={13} />Change
                </button>
              </div>
            );
          })}
        </div>
      </div>

      {/* ---- Usage analytics ---- */}
      <div className="card">
        <div className="card-head">
          <div>
            <h2>Model usage</h2>
            <p>Tokens, cost, and sessions per model over the selected window.</p>
          </div>
          <div className="tabs">
            {WINDOWS.map(([v, label]) => (
              <button key={v} className={cls("tab", days === v && "on")} onClick={() => setDays(v)}>{label}</button>
            ))}
          </div>
        </div>

        {totals && (
          <div className="grid cols-4" style={{ marginBottom: 16 }}>
            <div className="stat card" style={{ gap: 4 }}>
              <div className="stat-label mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em" }}>Models used</div>
              <div style={{ fontSize: 24, fontWeight: 700 }}>{totals.distinct_models ?? anModels.length}</div>
            </div>
            <div className="stat card" style={{ gap: 4 }}>
              <div className="stat-label mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em" }}>Input tokens</div>
              <div style={{ fontSize: 24, fontWeight: 700 }}>{fmtTokens(totals.total_input)}</div>
            </div>
            <div className="stat card" style={{ gap: 4 }}>
              <div className="stat-label mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em" }}>Output tokens</div>
              <div style={{ fontSize: 24, fontWeight: 700 }}>{fmtTokens(totals.total_output)}</div>
            </div>
            <div className="stat card" style={{ gap: 4 }}>
              <div className="stat-label mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em" }}>Est. cost</div>
              <div style={{ fontSize: 24, fontWeight: 700 }}>{fmtCost(totals.total_estimated_cost)}</div>
            </div>
          </div>
        )}

        {anLoading ? (
          <div className="empty"><span className="dotpulse" /> Loading usage…</div>
        ) : !anModels.length ? (
          <div className="empty">No model usage recorded in this window.</div>
        ) : (
          <div className="rows">
            {anModels.map((m) => {
              const caps = m.capabilities || {};
              return (
                <div className="row" key={m.model + (m.provider || "")} style={{ cursor: "default", alignItems: "flex-start" }}>
                  <span className="glyph"><Icon name="cpu" size={15} /></span>
                  <div className="row-main">
                    <div className="flex" style={{ gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                      <b className="mono" style={{ fontSize: 13 }}>{m.model}</b>
                      {m.provider && <span className="tag">{m.provider}</span>}
                      {caps.supports_vision && <span className="tag" title="vision">vision</span>}
                      {caps.supports_reasoning && <span className="tag" title="reasoning">reasoning</span>}
                      {caps.context_window ? <span className="tag" title="context window">{fmtCtx(caps.context_window)} ctx</span> : null}
                    </div>
                    <div className="row-sub mono" style={{ marginTop: 4 }}>
                      <span>in {fmtTokens(m.input_tokens)}</span><span> · out {fmtTokens(m.output_tokens)}</span>
                      <span> · cache {fmtTokens(m.cache_read_tokens)}</span>
                      <span> · {m.sessions} sess</span><span> · {m.api_calls} calls</span>
                      <span> · {fmtAgo(m.last_used_at)}</span>
                    </div>
                  </div>
                  <span className="badge pri" style={{ alignSelf: "center" }}>{fmtCost(m.estimated_cost)}</span>
                </div>
              );
            })}
          </div>
        )}
      </div>

      <ModelPicker
        open={!!picker}
        title={picker && picker.scope === "main" ? "Choose main model" : `Choose model · ${picker ? (AUX_LABEL[picker.task] || picker.task) : ""}`}
        sub={picker && picker.scope === "main" ? "Applies to new sessions." : "Override the model for this background task."}
        options={options}
        current={picker && picker.current}
        allowAuto={picker && picker.scope === "auxiliary"}
        busy={busy}
        onPick={(sel) => apply(sel, picker)}
        onClose={() => setPicker(null)}
      />
      <ConfirmExpensive data={confirm} busy={busy} onConfirm={confirmExpensive} onCancel={() => setConfirm(null)} />
    </div>
  );
}
window.PageModels = PageModels;
