// ===== Evals page =====

const { useState: eUseState, useEffect: eUseEffect, useMemo: eUseMemo, useRef: eUseRef } = React;

function eRelTime(ts) {
  if (!ts) return "—";
  const d = (Date.now() - ts) / 1000;
  if (d < 0) return "just now";
  if (d < 60) return `${Math.floor(d)}s ago`;
  if (d < 3600) return `${Math.floor(d / 60)}m ago`;
  if (d < 86400) return `${Math.floor(d / 3600)}h ago`;
  return `${Math.floor(d / 86400)}d ago`;
}
function fmtCost(c) { return `$${Number(c || 0).toFixed(4)}`; }
function evalStatusLabel(status) {
  return ({ pending: "Queued", running: "Running", complete: "Completed", failed: "Failed", cancelled: "Cancelled" })[status] || status || "Unknown";
}
function evalStatusClass(status, passRate) {
  if (status === "failed" || status === "cancelled") return "err";
  if (status === "pending" || status === "running") return "warn";
  return passRate >= 0.9 ? "ok" : passRate >= 0.7 ? "warn" : "err";
}

// ====== Datasets tab ======

function DatasetsTab({ datasets, refreshDatasets, agents, models, toolOptions, onRunStarted, loading }) {
  const [selectedId, setSelectedId] = eUseState(datasets[0]?.id || null);
  const [innerTab, setInnerTab] = eUseState("cases"); // cases | graders | settings
  const [selectedCaseId, setSelectedCaseId] = eUseState(null);
  const [addingCase, setAddingCase] = eUseState(false);
  const [runningDataset, setRunningDataset] = eUseState(null);

  eUseEffect(() => {
    if (datasets.length && !datasets.find((d) => d.id === selectedId)) {
      setSelectedId(datasets[0].id);
    }
  }, [datasets]);

  const ds = datasets.find((d) => d.id === selectedId);

  const createDs = async () => {
    const name = prompt("Dataset name?");
    if (!name) return;
    try {
      const created = await window.api.createDataset({ name });
      await refreshDatasets();
      setSelectedId(created.dataset_id);
    } catch (err) {
      alert("Create failed: " + (err.message || err));
    }
  };

  const deleteDs = async () => {
    if (!ds || !confirm(`Delete dataset "${ds.name}"?`)) return;
    try {
      await window.api.deleteDataset(ds.id);
      await refreshDatasets();
    } catch (err) {
      alert("Delete failed: " + (err.message || err));
    }
  };

  const deleteCase = async (caseId) => {
    if (!ds) return;
    try {
      await window.api.deleteDatasetCase(ds.id, caseId);
      await refreshDatasets();
    } catch (err) {
      alert("Delete case failed: " + (err.message || err));
    }
  };

  const toggleGrader = async (graderId) => {
    if (!ds) return;
    const nextGraders = ds.graders.includes(graderId)
      ? ds.graders.filter((g) => g !== graderId)
      : [...ds.graders, graderId];
    try {
      await window.api.updateDataset(ds.id, { graders: nextGraders });
      await refreshDatasets();
    } catch (err) {
      alert("Save graders failed: " + (err.message || err));
    }
  };

  if (!ds) {
    return (
      <div className="evals-shell">
        <div className="evals-side">
          <div className="head"><div className="row1"><h3>Datasets</h3>
            <div style={{ flex: 1 }} />
            <button className="btn primary sm" onClick={createDs}><Icon name="plus" size={11} /> New</button>
          </div></div>
          <div className="empty"><h3>No datasets yet</h3><p>Create one or add a case from Traces.</p></div>
        </div>
        <div className="evals-main"><div className="empty"><p>—</p></div></div>
      </div>
    );
  }

  return (
    <div className="evals-shell">
      <div className="evals-side">
        <div className="head">
          <div className="row1">
            <h3>Datasets</h3>
            <span className="badge-pill mono">{datasets.length}</span>
            <div style={{ flex: 1 }} />
            <button className="btn primary sm" onClick={createDs}><Icon name="plus" size={11} /> New</button>
          </div>
        </div>
        <div className="scroll">
          {datasets.map((d) => (
            <div
              key={d.id}
              className={"ds-card" + (d.id === selectedId ? " active" : "")}
              onClick={() => { setSelectedId(d.id); setSelectedCaseId(null); }}
            >
              <div className="nm">{d.name}</div>
              {d.description && <div className="ds">{d.description}</div>}
              <div className="meta">
                <span><Icon name="list" size={10} /> {d.cases.length} cases</span>
                <span>·</span>
                <span><Icon name="wand" size={10} /> {d.graders.length} graders</span>
              </div>
              {d.tags?.length > 0 && (
                <div className="meta" style={{ marginTop: 6, gap: 4 }}>
                  {d.tags.map((t) => <span key={t} className="pill">{t}</span>)}
                </div>
              )}
            </div>
          ))}
        </div>
      </div>

      <div className="evals-main">
        <div className="evals-detail-head">
          <div className="top">
            <div style={{ flex: 1, minWidth: 0 }}>
              <h1>{ds.name}</h1>
              <div className="sub">dataset · {ds.id} · created {new Date(ds.createdAt).toLocaleDateString()}</div>
              {ds.description && <div style={{ fontSize: 13, color: "var(--text-muted)", marginTop: 8 }}>{ds.description}</div>}
            </div>
            <div className="actions">
              <button className="btn"><Icon name="copy" size={11} /> Duplicate</button>
              <button className="btn danger" onClick={deleteDs}><Icon name="trash" size={11} /> Delete</button>
              <button className="btn primary" onClick={() => setRunningDataset(ds)} disabled={!agents.length || !ds.cases.length}>
                <Icon name="play" size={11} /> Run eval
              </button>
            </div>
          </div>
          <div className="ds-stats">
            <div className="stat">Cases<span className="num">{ds.cases.length}</span></div>
            <div className="stat">Graders<span className="num">{ds.graders.length}</span></div>
            <div className="stat">From traces<span className="num">{ds.cases.filter((c) => c.source?.type === "trace").length}</span></div>
            <div className="stat">Updated<span className="num" style={{ fontSize: 13 }}>
              {ds.cases.length ? eRelTime(Math.max(...ds.cases.map((c) => c.addedAt))) : "—"}
            </span></div>
          </div>
        </div>

        <div className="evals-tabs-inner">
          <button className={innerTab === "cases" ? "active" : ""} onClick={() => setInnerTab("cases")}>
            Cases <span className="badge-pill mono" style={{ fontSize: 10.5, padding: "0 5px" }}>{ds.cases.length}</span>
          </button>
          <button className={innerTab === "graders" ? "active" : ""} onClick={() => setInnerTab("graders")}>
            Graders <span className="badge-pill mono" style={{ fontSize: 10.5, padding: "0 5px" }}>{ds.graders.length}</span>
          </button>
          <div style={{ flex: 1 }} />
          {innerTab === "cases" && (
            <button
              className="btn primary sm"
              style={{ alignSelf: "center", marginBottom: 4 }}
              onClick={() => setAddingCase(true)}
            >
              <Icon name="plus" size={11} /> Add case
            </button>
          )}
        </div>

        <div className="scroll">
          {innerTab === "cases" && (
            <CasesTable
              dataset={ds}
              selectedCaseId={selectedCaseId}
              onSelect={setSelectedCaseId}
              onDelete={deleteCase}
              onAddCase={() => setAddingCase(true)}
            />
          )}
          {innerTab === "graders" && (
            <div className="grader-list">
              {GRADER_LIBRARY.map((g) => {
                const on = ds.graders.includes(g.id);
                return (
                  <div className="grow" key={g.id}>
                    <div className={"ic " + g.kind}>
                      {g.kind === "deterministic" ? "=" : g.kind === "llm" ? "L" : g.kind === "trajectory" ? "→" : "$"}
                    </div>
                    <div className="meta">
                      <div className="nm">{g.name}</div>
                      <div className="ds">{g.desc}</div>
                    </div>
                    <div className={"switch" + (on ? " on" : "")} onClick={() => toggleGrader(g.id)}></div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>

      {addingCase && (
        <AddCaseModal
          dataset={ds}
          toolOptions={toolOptions}
          onClose={() => setAddingCase(false)}
          onAdded={() => refreshDatasets()}
        />
      )}
      {runningDataset && (
        <RunEvalModal
          dataset={runningDataset}
          agents={agents}
          models={models}
          onClose={() => setRunningDataset(null)}
          onStarted={onRunStarted}
        />
      )}
    </div>
  );
}

function CasesTable({ dataset, selectedCaseId, onSelect, onDelete, onAddCase }) {
  if (dataset.cases.length === 0) {
    return (
      <div className="empty" style={{ padding: 60 }}>
        <div className="icon"><Icon name="list" size={20} /></div>
        <h3>No cases yet</h3>
        <p>
          Add a case manually, or open a run in{" "}
          <a href="Traces.html">Traces</a> and click "Add to dataset".
        </p>
        <button className="btn primary" style={{ marginTop: 14 }} onClick={onAddCase}>
          <Icon name="plus" size={11} /> Add case
        </button>
      </div>
    );
  }

  const selected = dataset.cases.find((c) => c.id === selectedCaseId);

  return (
    <div>
      <table className="cases-table">
        <thead>
          <tr>
            <th style={{ width: 36 }}></th>
            <th>Input · expected</th>
            <th style={{ width: 180 }}>Expected tools</th>
            <th style={{ width: 110 }}>Source</th>
            <th style={{ width: 110 }}>Added</th>
            <th style={{ width: 60 }}></th>
          </tr>
        </thead>
        <tbody>
          {dataset.cases.map((c, i) => {
            const active = c.id === selectedCaseId;
            return (
              <React.Fragment key={c.id}>
                <tr className={active ? "active" : ""} onClick={() => onSelect(active ? null : c.id)}>
                  <td style={{ color: "var(--text-faint)", fontFamily: "var(--font-mono)", fontSize: 11 }}>
                    #{i + 1}
                  </td>
                  <td className="input-cell">
                    <span className="one-line">{c.input}</span>
                    {c.expected && <span className="expected">→ {c.expected}</span>}
                  </td>
                  <td className="tools-cell">
                    {c.tools.length === 0 ? <span className="dim">—</span> : c.tools.map((t, i) => (
                      <span key={t} className="badge-pill mono" style={{ marginRight: 4 }}>{t}</span>
                    ))}
                  </td>
                  <td className="source-cell">
                    {c.source?.type === "trace" ? (
                      <a className="src trace" href="Traces.html" onClick={(e) => e.stopPropagation()}>
                        <Icon name="play" size={9} /> trace
                      </a>
                    ) : (
                      <span className="src manual"><Icon name="user" size={9} /> manual</span>
                    )}
                  </td>
                  <td style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-muted)" }}>
                    {eRelTime(c.addedAt)}
                  </td>
                  <td className="actions-cell">
                    <button className="icbtn" onClick={(e) => { e.stopPropagation(); onDelete(c.id); }} title="Delete">
                      <Icon name="trash" size={12} />
                    </button>
                  </td>
                </tr>
                {active && (
                  <tr>
                    <td colSpan={6} style={{ padding: 0, background: "#fcfcfd" }}>
                      <div style={{ padding: "12px 18px 16px 60px" }}>
                        <div style={{ fontSize: 10.5, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600, marginBottom: 6 }}>Input</div>
                        <pre style={{ margin: "0 0 12px", background: "white", border: "1px solid var(--border)", borderRadius: 6, padding: "8px 10px", fontSize: 12.5, whiteSpace: "pre-wrap", lineHeight: 1.5 }}>{c.input}</pre>
                        {c.expected && (
                          <>
                            <div style={{ fontSize: 10.5, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600, marginBottom: 6 }}>Expected output</div>
                            <pre style={{ margin: "0 0 12px", background: "white", border: "1px solid var(--border)", borderRadius: 6, padding: "8px 10px", fontSize: 12.5, whiteSpace: "pre-wrap", lineHeight: 1.5 }}>{c.expected}</pre>
                          </>
                        )}
                        {c.tags?.length > 0 && (
                          <div style={{ marginBottom: 4 }}>
                            {c.tags.map((t) => <span key={t} className="badge-pill mono" style={{ marginRight: 4 }}>#{t}</span>)}
                          </div>
                        )}
                      </div>
                    </td>
                  </tr>
                )}
              </React.Fragment>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// ====== Runs tab ======

function RunsTab({ runs, datasets }) {
  const [selectedId, setSelectedId] = eUseState(runs[0]?.id || null);
  const [compareId, setCompareId] = eUseState(null);
  const selected = runs.find((r) => r.id === selectedId) || null;
  const compared = runs.find((r) => r.id === compareId) || null;

  if (runs.length === 0) {
    return (
      <div className="evals-shell">
        <div className="evals-side"><div className="head"><h3>Eval runs</h3></div></div>
        <div className="evals-main"><div className="empty"><h3>No eval runs yet</h3><p>Pick a dataset and click "Run eval".</p></div></div>
      </div>
    );
  }

  return (
    <div className="evals-shell" style={{ gridTemplateColumns: "1fr" }}>
      <div className="evals-main">
        <div style={{ flex: 1, overflowY: "auto" }}>
          <div style={{
            padding: "10px 24px",
            borderBottom: "1px solid var(--border)",
            display: "grid",
            gridTemplateColumns: "18px 1fr 220px 140px 90px 90px",
            gap: 12,
            fontSize: 10.5, color: "var(--text-muted)",
            textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600,
            background: "var(--bg-subtle)",
          }}>
            <span></span>
            <span>Run</span>
            <span>Pass rate</span>
            <span style={{ textAlign: "right" }}>Started</span>
            <span style={{ textAlign: "right" }}>Latency</span>
            <span style={{ textAlign: "right" }}>Cost</span>
          </div>
          {runs.map((r) => {
            const summary = r.summary || {};
            const status = r.status || "pending";
            const expectedCaseCount = datasets.find((ds) => ds.id === r.datasetId)?.cases.length ?? r.cases.length;
            const pass = summary.passed_count ?? r.cases.filter((c) => c.pass).length;
            const passRate = summary.pass_rate ?? (r.cases.length ? pass / r.cases.length : 0);
            const dotCls = evalStatusClass(status, passRate);
            const p50Latency = summary.p50_latency_ms ?? 0;
            const totalCost = summary.total_cost_usd ?? r.cases.reduce((s, c) => s + (c.costUSD || 0), 0);
            return (
              <div
                key={r.id}
                className={"run-row" + (r.id === selectedId ? " active" : "")}
                onClick={() => setSelectedId(r.id === selectedId ? null : r.id)}
              >
                <span className={"status-dot " + dotCls}></span>
                <div className="main">
                  <div className="ttl">
                    {r.datasetName}
                    <span className="badge-pill mono">{r.datasetVersion}</span>
                    <span className={"badge-pill mono " + dotCls}>{evalStatusLabel(status)}</span>
                  </div>
                  <div className="meta">
                    <Icon name="user" size={10} /> {r.agentName} · {r.model} · {r.cases.length}/{expectedCaseCount} cases
                  </div>
                  {r.error && <div className="meta" style={{ color: "#b91c1c", marginTop: 3 }}>{r.error}</div>}
                </div>
                <div>
                  <div className="passbar">
                    <span className="fill" style={{ width: (passRate * 100).toFixed(1) + "%" }}></span>
                  </div>
                  <div className="pass-label">
                    {status === "pending" || status === "running"
                      ? `${evalStatusLabel(status)} · ${r.cases.length} case(s)`
                      : status === "failed"
                        ? `Failed · ${summary.error_count || 0} case error(s)`
                        : `${(passRate * 100).toFixed(0)}% · ${pass}/${expectedCaseCount} pass`}
                  </div>
                </div>
                <div className="dur">{eRelTime(r.startedAt)}</div>
                <div className="dur">p50 {fmtMs(p50Latency)}</div>
                <div className="cost">{fmtCost(totalCost)}</div>
              </div>
            );
          })}

          {selected && <RunDetail run={selected} runs={runs} datasets={datasets} />}
        </div>
      </div>
    </div>
  );
}

function RunDetail({ run, runs, datasets }) {
  const ds = datasets.find((d) => d.id === run.datasetId);
  const summary = run.summary || {};
  const expectedCaseCount = ds?.cases.length ?? run.cases.length;
  const pass = summary.passed_count ?? run.cases.filter((c) => c.pass).length;
  const passRate = summary.pass_rate ?? (run.cases.length ? pass / run.cases.length : 0);
  const totalCost = summary.total_cost_usd ?? run.cases.reduce((s, c) => s + (c.costUSD || 0), 0);
  const status = run.status || "pending";

  const otherRuns = runs.filter((r) => r.datasetId === run.datasetId && r.id !== run.id);
  const baseline = otherRuns[0] || null;
  const baselinePass = baseline ? baseline.cases.filter((c) => c.pass).length / baseline.cases.length : null;

  return (
    <div style={{
      background: "var(--bg-subtle)", borderTop: "1px solid var(--border)",
      padding: "0", marginTop: 0,
    }}>
      <div className="compare-summary">
        <div className="kpi">
          <span className="k">Status</span>
          <span className="v" style={{ color: evalStatusClass(status, passRate) === "err" ? "#b91c1c" : "var(--text)" }}>
            {evalStatusLabel(status)}
          </span>
        </div>
        <div className="kpi">
          <span className="k">Pass rate</span>
          <span className="v" style={{ color: passRate >= 0.8 ? "var(--accent)" : passRate >= 0.5 ? "var(--orange)" : "#b91c1c" }}>
            {(passRate * 100).toFixed(0)}<span className="pct">%</span>
          </span>
        </div>
        {baselinePass != null && (
          <div className="kpi">
            <span className="k">vs previous</span>
            <span className="v" style={{ color: passRate - baselinePass >= 0 ? "var(--accent)" : "#b91c1c" }}>
              {passRate - baselinePass >= 0 ? "+" : ""}{((passRate - baselinePass) * 100).toFixed(0)}<span className="pct">%</span>
            </span>
          </div>
        )}
        <div className="kpi">
          <span className="k">Cases</span>
          <span className="v">{pass}<span className="pct">/{expectedCaseCount}</span></span>
        </div>
        <div className="kpi">
          <span className="k">p50 / p95 latency</span>
          <span className="v">{fmtMs(summary.p50_latency_ms)} / {fmtMs(summary.p95_latency_ms)}</span>
        </div>
        <div className="kpi">
          <span className="k">Total cost</span>
          <span className="v">{fmtCost(totalCost)}</span>
        </div>
        <div className="kpi">
          <span className="k">Input / output tokens</span>
          <span className="v">{(summary.input_tokens || 0).toLocaleString()} / {(summary.output_tokens || 0).toLocaleString()}</span>
        </div>
        <div style={{ flex: 1 }} />
        <div className="kpi" style={{ alignItems: "flex-end" }}>
          <span className="k">Run id</span>
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, color: "var(--text)" }}>{run.id}</span>
        </div>
      </div>

      {run.error && (
        <div style={{ margin: "0 18px 14px", padding: "10px 12px", border: "1px solid #fecaca", background: "#fff1f2", color: "#991b1b", fontSize: 12.5, borderRadius: 6 }}>
          {run.error}
        </div>
      )}

      <table className="run-cases-table">
        <thead>
          <tr>
            <th style={{ width: 60 }}>Result</th>
            <th>Case</th>
            <th>Grader scores</th>
            <th style={{ width: 80, textAlign: "right" }}>Latency</th>
            <th style={{ width: 80, textAlign: "right" }}>Cost</th>
            <th style={{ width: 60 }}></th>
          </tr>
        </thead>
        <tbody>
          {run.cases.length === 0 && (
            <tr><td colSpan={6} style={{ padding: "18px 16px", color: "var(--text-muted)" }}>
              {status === "pending" || status === "running" ? "Waiting for the first case to start." : "No case result was recorded."}
            </td></tr>
          )}
          {run.cases.map((rc) => {
            const dsCase = ds?.cases.find((c) => c.id === rc.caseId);
            const caseFailed = !!rc.error || rc.pass === false;
            const casePending = rc.pass == null && !rc.error;
            return (
              <tr key={rc.caseId}>
                <td>
                  <span className={"pass-pill " + (casePending ? "warn" : caseFailed ? "fail" : "ok")}>
                    {casePending ? "…" : caseFailed ? <Icon name="x" size={10} /> : <Icon name="check" size={10} />}
                    {casePending ? "running" : caseFailed ? "fail" : "pass"}
                  </span>
                </td>
                <td>
                  <div style={{ fontSize: 13 }}>{dsCase?.input || rc.caseId}</div>
                  {rc.error && <div style={{ fontSize: 11, color: "#b91c1c", marginTop: 3 }}>{rc.error}</div>}
                  {dsCase?.expected && (
                    <div style={{ fontSize: 11, color: "var(--text-muted)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 480 }}>
                      → {dsCase.expected}
                    </div>
                  )}
                </td>
                <td className="score-cell">
                  {Object.entries(rc.scores).map(([k, v]) => {
                    const isPass = typeof v === "number" ? v >= (k.startsWith("llm") ? 3.5 : 1) : !!v;
                    return (
                      <span key={k} className={"score-pill " + (isPass ? "ok" : "fail")}>
                        {k}: {typeof v === "number" ? (k.startsWith("llm") ? v.toFixed(1) : v) : (v ? "✓" : "✗")}
                      </span>
                    );
                  })}
                </td>
                <td className="dur" style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, textAlign: "right" }}>{fmtMs(rc.latencyMs)}</td>
                <td className="dur" style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, textAlign: "right" }}>{fmtCost(rc.costUSD)}</td>
                <td style={{ textAlign: "right" }}>
                  <button className="btn ghost sm" title="View trace"><Icon name="play" size={11} /></button>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// ====== Main app ======

function EvalsApp() {
  const [tab, setTab] = eUseState("datasets"); // datasets | runs
  const [datasets, setDatasets] = eUseState([]);
  const [runs, setRuns] = eUseState([]);
  const [agents, setAgents] = eUseState([]);
  const [models, setModels] = eUseState([]);
  const [toolOptions, setToolOptions] = eUseState([]);
  const [loading, setLoading] = eUseState(true);
  const [loadError, setLoadError] = eUseState(null);
  // PR-S.17d · topbar 上 Users 入口 admin-gate
  const [isAdmin, setIsAdmin] = eUseState(false);
  eUseEffect(() => {
    window.api.authMe()
      .then((m) => setIsAdmin(!!m.is_admin))
      .catch(() => {});
  }, []);

  // Detail (cases) isn't in the list response, so we fan out one
  // getDataset() per row. Acceptable for current demo scale; future PR
  // can add `?include=cases` to the list endpoint.
  const refreshDatasets = async () => {
    const items = await window.api.listDatasets();
    const detailed = await Promise.all(items.map(
      (ds) => window.api.getDataset(ds.dataset_id).catch(() => ({ ...ds, cases: [] }))
    ));
    setDatasets(detailed.map(window.decorateDataset));
  };
  const refreshRuns = async () => {
    const items = await window.api.listEvalRuns({ limit: 200 });
    setRuns(items.map(window.decorateEvalRun));
  };
  const refreshRunOptions = async () => {
    const [agentItems, modelResponse, localToolItems, mcpItems] = await Promise.all([
      window.api.listAgents(),
      window.api.listModels(),
      window.api.listLocalTools().catch(() => []),
      window.api.listMcpServers().catch(() => []),
    ]);
    setAgents(agentItems);
    setModels(modelResponse.items || []);
    const names = new Set((localToolItems || []).map((tool) => tool.name).filter(Boolean));
    (mcpItems || []).forEach((server) => (server.tools || []).forEach((tool) => {
      const name = typeof tool === "string" ? tool : (tool?.name || tool?.id);
      if (name) names.add(name);
    }));
    setToolOptions([...names].sort().map((name) => ({ id: name, name, label: name })));
    window.MODELS = modelResponse.items || [];
    window.MODEL_PROVIDERS = modelResponse.providers || [];
  };
  eUseEffect(() => {
    (async () => {
      try {
        await Promise.all([refreshDatasets(), refreshRuns(), refreshRunOptions()]);
      } catch (err) {
        console.warn("evals: initial load failed", err);
        setLoadError(err.message || String(err));
      } finally {
        setLoading(false);
      }
    })();
  }, []);

  eUseEffect(() => {
    if (!runs.some((run) => run.status === "pending" || run.status === "running")) return undefined;
    const interval = window.setInterval(() => refreshRuns().catch(() => {}), 1000);
    return () => window.clearInterval(interval);
  }, [runs]);

  return (
    <div className="app">
      <div className="topbar">
        <div className="brand">
          <div className="dot">百</div>
          baizhi
          <span style={{ color: "var(--text-faint)" }}>/</span>
          <span style={{ fontWeight: 500, color: "var(--text-muted)" }}>Evals</span>
        </div>
        <div className="tabs">
          <a href="/playground" style={navLinkStyle()}>Playground</a>
          <a href="/manage" style={navLinkStyle()}>Manage</a>
          <button className="active">Evals</button>
          <a href="/traces" style={navLinkStyle()}>Traces</a>
          <a href="/marketplace" style={navLinkStyle()}>Marketplace</a>
          <a href="/apps" style={navLinkStyle()}>Apps</a>
          {isAdmin && <a href="/users" style={navLinkStyle()}>Users</a>}
          <a href="/api-docs" style={navLinkStyle()}>API&nbsp;Docs</a>
        </div>
        <div className="spacer" />
        <div className="actions">
          <button className="btn ghost sm"><Icon name="history" size={12} /> History</button>
          <button className="btn sm"><Icon name="share" size={12} /> Export</button>
        </div>
      </div>

      <div className="evals-tabs">
        <button className={tab === "datasets" ? "active" : ""} onClick={() => setTab("datasets")}>
          <Icon name="list" size={12} /> Datasets
          <span className="count">{datasets.length}</span>
        </button>
        <button className={tab === "runs" ? "active" : ""} onClick={() => setTab("runs")}>
          <Icon name="play" size={12} /> Eval runs
          <span className="count">{runs.length}</span>
        </button>
      </div>

      {loadError && (
        <div style={{
          padding: "6px 16px", background: "#fee2e2", color: "#991b1b",
          fontSize: 12, fontFamily: "var(--font-mono)",
          borderBottom: "1px solid #fecaca",
        }}>
          ⚠ Failed to load from backend: {loadError}. Datasets / Eval runs may be empty.
        </div>
      )}
      {tab === "datasets"
        ? <DatasetsTab
            datasets={datasets}
            refreshDatasets={refreshDatasets}
            agents={agents}
            models={models}
            toolOptions={toolOptions}
            onRunStarted={async () => { await refreshRuns(); setTab("runs"); }}
            loading={loading}
          />
        : <RunsTab runs={runs} datasets={datasets} />}

      <ToastContainer />
    </div>
  );
}

function navLinkStyle() {
  return {
    background: "transparent", border: 0, padding: "6px 10px", borderRadius: 6,
    color: "var(--text-muted)", fontSize: 13, textDecoration: "none", cursor: "pointer",
  };
}

ReactDOM.createRoot(document.getElementById("root")).render(<EvalsApp />);
