/* global React, Button, Icon, LoadingSpinner, Pill, Select, EmptyState, Drawer, useApiResource, apiFetch, formatRelative, formatDuration, shortId, useToast, useAuth, canUseTenantAction, tenantActionDisabledReason, workflowTesterTargetLimit, targetsOverWorkflowLimit, WorkflowTargetPicker, useEscapeToClose, SaTlcOpChip, SaTlcStatusPill */
//
// Experiments area
// ----------------
// "Did the change improve the MCP?" An experiment runs the same workflow
// set across the same harness × model paths against a baseline MCP server
// and a variant, then compares pass rate, quality (avg evaluation score),
// and efficiency (duration, tool calls) — and flags regressions.
//
//   /experiments      → experiments list table.
//   /experiments/:id  → same list with the comparison drawer open
//                       (deep-linkable, refresh-safe).
//
// Fan-out reuses the benchmark batch machinery server-side; each run row
// links to /runs/:id like benchmark cells do.

const {
  useEffect: useEffectX,
  useMemo: useMemoX,
  useState: useStateX,
} = React;

function expFmtPct(rate) {
  if (rate == null || !Number.isFinite(Number(rate))) return '—';
  return `${(Number(rate) * 100).toFixed(1)}%`;
}

function expFmtScore(score) {
  if (score == null || !Number.isFinite(Number(score))) return '—';
  return Number(score).toFixed(1);
}

// Signed delta chip. `inverse` flips the good direction for
// lower-is-better metrics (duration, tool calls).
function ExperimentDelta({ value, suffix = 'pt', scale = 100, digits = 1, inverse = false }) {
  if (value == null || !Number.isFinite(Number(value))) {
    return <span className="experiment-delta flat">—</span>;
  }
  const scaled = Number(value) * scale;
  const rounded = Number(scaled.toFixed(digits));
  const good = inverse ? rounded < 0 : rounded > 0;
  const cls = rounded === 0 ? 'flat' : good ? 'up' : 'down';
  return (
    <span className={`experiment-delta ${cls}`}>
      {rounded > 0 ? '+' : ''}{rounded.toFixed(digits)}{suffix}
    </span>
  );
}

const EXPERIMENT_STATUS_TONES = {
  queued: 'neutral',
  running: 'info',
  completed: 'ok',
  failed: 'bad',
  canceled: 'neutral',
};

function ExperimentStatusPill({ experiment }) {
  if (experiment.decision === 'accepted') return <Pill tone="ok">Accepted</Pill>;
  if (experiment.decision === 'rejected') return <Pill tone="bad">Rejected</Pill>;
  const status = experiment.effective_status || experiment.status;
  return <Pill tone={EXPERIMENT_STATUS_TONES[status] || 'neutral'}>{status}</Pill>;
}

// ---------------------------------------------------------------------------
// Comparison drawer
// ---------------------------------------------------------------------------

function ExperimentArmCells({ row }) {
  return (
    <>
      <td className="num mono muted">{expFmtPct(row.baseline?.passRate)}</td>
      <td className="num mono">{expFmtPct(row.variant?.passRate)}</td>
      <td className="num"><ExperimentDelta value={row.deltas?.passRate} /></td>
    </>
  );
}

function ExperimentDrawer({ experimentId, navigate, onClose, onChanged }) {
  const toast = useToast();
  const auth = useAuth();
  const canDecide = canUseTenantAction(auth, 'editor');
  const detail = useApiResource(`/api/experiments/${experimentId}`);
  const [deciding, setDeciding] = useStateX(false);
  const experiment = detail.data?.experiment || null;
  const comparison = detail.data?.comparison || null;
  const pendingRuns = comparison?.pendingRuns || 0;

  // Poll while runs are still in flight so the comparison fills in live.
  useEffectX(() => {
    if (!pendingRuns) return undefined;
    const timer = window.setInterval(() => detail.reload(), 15000);
    return () => window.clearInterval(timer);
  }, [pendingRuns, detail.reload]);

  async function decide(decision) {
    if (deciding) return;
    setDeciding(true);
    try {
      await apiFetch(`/api/experiments/${experimentId}`, {
        method: 'PATCH',
        body: JSON.stringify({ decision }),
      });
      toast.show({
        tone: decision === 'accepted' ? 'good' : 'info',
        title: decision === 'accepted' ? 'Variant accepted' : 'Variant rejected',
      });
      await detail.reload();
      onChanged?.();
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not save decision', description: error.message });
    } finally {
      setDeciding(false);
    }
  }

  const overall = comparison?.overall;
  const regressions = comparison?.regressions || [];

  return (
    <Drawer
      open
      onClose={onClose}
      size="lg"
      title={experiment ? experiment.name : 'Experiment'}
      subtitle={experiment ? (
        <span className="experiment-drawer-sub">
          <ExperimentStatusPill experiment={experiment} />
          <span className="muted">
            {shortId(experiment.id)}
            {experiment.created_at ? ` · ${formatRelative(experiment.created_at)}` : ''}
          </span>
        </span>
      ) : null}
      footer={experiment && !experiment.decision ? (
        <div className="experiment-drawer-actions">
          <Button
            disabled={!canDecide || deciding}
            title={!canDecide ? tenantActionDisabledReason(auth, 'editor') : undefined}
            onClick={() => decide('rejected')}>
            Reject variant
          </Button>
          <Button
            variant="primary"
            disabled={!canDecide || deciding}
            title={!canDecide ? tenantActionDisabledReason(auth, 'editor') : undefined}
            loading={deciding}
            onClick={() => decide('accepted')}>
            <Icon name="check" size={13} />
            Accept variant
          </Button>
        </div>
      ) : null}>
      {detail.loading && !detail.data ? (
        <div className="code inline-loading">
          <LoadingSpinner size="sm" label="Loading experiment" decorative />
          Loading experiment...
        </div>
      ) : detail.error ? (
        <EmptyState title="Could not load experiment" body={detail.error.message} />
      ) : experiment ? (
        <>
          {experiment.hypothesis && (
            <section className="experiment-drawer-sec">
              <div className="ui-section-title">Hypothesis</div>
              <div className="experiment-hypothesis">{experiment.hypothesis}</div>
            </section>
          )}

          {experiment.tool_list_change_id && experiment.tool_list_change_title && (
            <section className="experiment-drawer-sec">
              <div className="ui-section-title">Tool list change</div>
              {/* The structured change this experiment validates (174/#1110).
                  Diff rows reuse the Tool registry's vocabulary so the change
                  reads identically on both surfaces. */}
              <div className="experiment-tlc-head">
                <span className="experiment-tlc-title">{experiment.tool_list_change_title}</span>
                <SaTlcStatusPill status={experiment.tool_list_change_status} />
              </div>
              <div className="sa-tlc-diff">
                {(experiment.tool_list_change_edits || []).map((edit, index) => (
                  <div className="sa-tlc-diff-row" key={index}>
                    <SaTlcOpChip op={edit.op} />
                    <span className="mono sa-tlc-diff-tool">{edit.tool_name}</span>
                    {(edit.note || edit.after?.description) && (
                      <span className="sa-tlc-diff-note">— {edit.note || edit.after.description}</span>
                    )}
                  </div>
                ))}
              </div>
            </section>
          )}

          {pendingRuns > 0 && (
            <div className="experiment-pending-note">
              <LoadingSpinner size="sm" label="Runs in flight" decorative />
              {pendingRuns} run{pendingRuns === 1 ? '' : 's'} still in flight — results update live.
            </div>
          )}

          <section className="experiment-drawer-sec">
            <div className="ui-section-title">Pass rate — baseline vs variant</div>
            <div className="experiment-compare">
              <div className="experiment-compare-side">
                <div className="experiment-compare-label">
                  Baseline · {experiment.baseline_mcp_server_name || 'baseline'}
                </div>
                <div className="experiment-compare-val">{expFmtPct(overall?.baseline?.passRate)}</div>
                <div className="experiment-compare-foot">
                  {overall?.baseline?.runs || 0} scored runs
                </div>
              </div>
              <div className="experiment-compare-arrow">
                <Icon name="chevronsRight" size={18} />
                <ExperimentDelta value={overall?.deltas?.passRate} />
              </div>
              <div className="experiment-compare-side is-variant">
                <div className="experiment-compare-label">
                  Variant · {experiment.variant_mcp_server_name || 'variant'}
                </div>
                <div className="experiment-compare-val">{expFmtPct(overall?.variant?.passRate)}</div>
                <div className="experiment-compare-foot">
                  {overall?.variant?.runs || 0} scored runs
                </div>
              </div>
            </div>
          </section>

          <section className="experiment-drawer-sec">
            <div className="ui-section-title">Quality &amp; efficiency signals</div>
            <div className="experiment-metrics">
              <div className="experiment-metric">
                <div className="experiment-metric-label">Avg score (0–5)</div>
                <div className="experiment-metric-val">
                  {expFmtScore(overall?.baseline?.avgScore)} → {expFmtScore(overall?.variant?.avgScore)}
                  {' '}<ExperimentDelta value={overall?.deltas?.avgScore} suffix="" scale={1} />
                </div>
              </div>
              <div className="experiment-metric">
                <div className="experiment-metric-label">Avg duration</div>
                <div className="experiment-metric-val">
                  {overall?.baseline?.avgDurationMs != null ? formatDuration(overall.baseline.avgDurationMs) : '—'}
                  {' → '}
                  {overall?.variant?.avgDurationMs != null ? formatDuration(overall.variant.avgDurationMs) : '—'}
                </div>
              </div>
              <div className="experiment-metric">
                <div className="experiment-metric-label">Avg tool calls / run</div>
                <div className="experiment-metric-val">
                  {expFmtScore(overall?.baseline?.avgToolCalls)} → {expFmtScore(overall?.variant?.avgToolCalls)}
                  {' '}<ExperimentDelta value={overall?.deltas?.avgToolCalls} suffix="" scale={1} inverse />
                </div>
              </div>
            </div>
          </section>

          <section className="experiment-drawer-sec">
            <div className="ui-section-title">Results by workflow</div>
            <table className="ui-table experiment-results-table">
              <thead>
                <tr><th>Workflow</th><th className="num">Baseline</th><th className="num">Variant</th><th className="num">Δ</th></tr>
              </thead>
              <tbody>
                {(comparison?.byWorkflow || []).map((row) => (
                  <tr key={row.key}>
                    <td>{row.workflowName || shortId(row.workflowId)}</td>
                    <ExperimentArmCells row={row} />
                  </tr>
                ))}
              </tbody>
            </table>
          </section>

          <section className="experiment-drawer-sec">
            <div className="ui-section-title">Results by harness × model</div>
            <table className="ui-table experiment-results-table">
              <thead>
                <tr><th>Path</th><th className="num">Baseline</th><th className="num">Variant</th><th className="num">Δ</th></tr>
              </thead>
              <tbody>
                {(comparison?.byPath || []).map((row) => (
                  <tr key={row.key}>
                    <td>
                      <span className="mono text-xs">
                        {row.testerHarness || 'unknown'} · {row.testerModelName || row.testerModelId || 'unknown'}
                      </span>
                    </td>
                    <ExperimentArmCells row={row} />
                  </tr>
                ))}
              </tbody>
            </table>
          </section>

          <section className="experiment-drawer-sec">
            <div className="ui-section-title">Regressions</div>
            {regressions.length === 0 ? (
              <div className="experiment-no-regressions">
                <Icon name="check" size={13} />
                No regressions detected across {(comparison?.byPath || []).length} path{(comparison?.byPath || []).length === 1 ? '' : 's'} and {(comparison?.byWorkflow || []).length} workflow{(comparison?.byWorkflow || []).length === 1 ? '' : 's'}.
              </div>
            ) : (
              <table className="ui-table experiment-results-table">
                <thead>
                  <tr><th>Cell</th><th>Kind</th><th className="num">Baseline</th><th className="num">Variant</th></tr>
                </thead>
                <tbody>
                  {regressions.map((reg, index) => (
                    <tr key={index}>
                      <td>
                        <div>{reg.workflowName || shortId(reg.workflowId)}</div>
                        <div className="mono text-xs muted">
                          {reg.testerHarness || 'unknown'} · {reg.testerModelName || reg.testerModelId || 'unknown'}
                        </div>
                      </td>
                      <td><Pill tone="bad">{reg.kind === 'pass_rate' ? 'Pass rate' : reg.kind === 'quality' ? 'Quality' : 'Efficiency'}</Pill></td>
                      <td className="num mono">
                        {reg.kind === 'efficiency'
                          ? formatDuration(reg.baseline?.avgDurationMs || 0)
                          : reg.kind === 'quality'
                            ? expFmtScore(reg.baseline?.avgScore)
                            : expFmtPct(reg.baseline?.passRate)}
                      </td>
                      <td className="num mono">
                        {reg.kind === 'efficiency'
                          ? formatDuration(reg.variant?.avgDurationMs || 0)
                          : reg.kind === 'quality'
                            ? expFmtScore(reg.variant?.avgScore)
                            : expFmtPct(reg.variant?.passRate)}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </section>
        </>
      ) : null}
    </Drawer>
  );
}

// ---------------------------------------------------------------------------
// New experiment modal
// ---------------------------------------------------------------------------

function NewExperimentDialog({ onClose, onCreated, initialToolListChangeId = null }) {
  const toast = useToast();
  const auth = useAuth();
  const workflows = useApiResource('/api/workflows');
  const sources = useApiResource('/api/mcp-servers');
  const catalog = useApiResource('/api/catalog/models');
  // Tool-list changes live in the Session Analytics surface (mcp_analytics
  // flag); the endpoint 403s without it, so don't even fetch. The picker is
  // purely optional — an experiment without a TLC is a plain A/B of two
  // servers.
  const mcpAnalyticsEnabled = auth.me?.featureFlags?.mcpAnalytics === true;
  const toolListChanges = useApiResource(mcpAnalyticsEnabled ? '/api/mcp-analytics/v2/tool-list-changes' : null);
  const [name, setName] = useStateX('');
  const [hypothesis, setHypothesis] = useStateX('');
  const [workflowIds, setWorkflowIds] = useStateX([]);
  const [baselineId, setBaselineId] = useStateX('');
  const [variantId, setVariantId] = useStateX('');
  const [toolListChangeId, setToolListChangeId] = useStateX('');
  const [testerTargets, setTesterTargets] = useStateX([]);
  const [saving, setSaving] = useStateX(false);

  const workflowRows = useMemoX(
    () => (workflows.data?.rows || []).filter((row) => row.is_active !== false),
    [workflows.data],
  );
  const sourceRows = useMemoX(
    () => (sources.data?.rows || []).filter((row) => row.is_active !== false),
    [sources.data],
  );
  const models = catalog.data?.models || [];
  const targetLimit = workflowTesterTargetLimit(auth);
  // Only open changes are attachable (mirrors the server-side guard).
  const attachableChanges = useMemoX(
    () => (toolListChanges.data?.changes || [])
      .filter((change) => ['draft', 'ready', 'in_experiment'].includes(change.status)),
    [toolListChanges.data],
  );

  function pickToolListChange(id) {
    setToolListChangeId(id);
    const change = attachableChanges.find((c) => c.id === id);
    if (!change) return;
    // A TLC names the change and the server it edits — prefill the empty
    // fields so picking one is a one-click setup, never an overwrite.
    if (!name.trim()) setName(change.title);
    if (!baselineId && sourceRows.some((row) => row.id === change.mcp_server_id)) {
      setBaselineId(change.mcp_server_id);
    }
  }

  // "Launch experiment" deep link from a tool-list change card: apply the
  // same pick-and-prefill once the changes and servers have loaded. One-shot
  // — after the user touches the picker themselves, never re-apply.
  const [initialTlcApplied, setInitialTlcApplied] = useStateX(false);
  useEffectX(() => {
    if (initialTlcApplied || !initialToolListChangeId) return;
    if (toolListChanges.loading || sources.loading) return;
    setInitialTlcApplied(true);
    if (attachableChanges.some((c) => c.id === initialToolListChangeId)) {
      pickToolListChange(initialToolListChangeId);
    }
  }, [initialTlcApplied, initialToolListChangeId, toolListChanges.loading, sources.loading, attachableChanges]);

  function toggleWorkflow(id) {
    setWorkflowIds((current) => (
      current.includes(id) ? current.filter((x) => x !== id) : [...current, id]
    ));
  }

  const totalRuns = workflowIds.length * testerTargets.length * 2;
  const formInvalid = !name.trim()
    || workflowIds.length === 0
    || testerTargets.length === 0
    || !baselineId
    || !variantId
    || baselineId === variantId
    || targetsOverWorkflowLimit(testerTargets, targetLimit);

  async function submit(event) {
    event.preventDefault();
    if (formInvalid || saving) return;
    setSaving(true);
    try {
      const result = await apiFetch('/api/experiments', {
        method: 'POST',
        body: JSON.stringify({
          name: name.trim(),
          hypothesis: hypothesis.trim() || null,
          workflowIds,
          testerTargets,
          baselineMcpServerId: baselineId,
          variantMcpServerId: variantId,
          toolListChangeId: toolListChangeId || null,
        }),
      });
      toast.show({
        tone: 'good',
        title: 'Experiment dispatched',
        description: `${result.summary?.queued ?? 0} runs queued across baseline and variant.`,
      });
      onCreated(result.experiment);
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not start experiment', description: error.message });
      setSaving(false);
    }
  }

  useEscapeToClose({ disabled: saving, onClose });

  const sourceOptions = sourceRows.map((row) => ({
    value: row.id,
    label: `${row.name}${row.environment ? ` · ${row.environment}` : ''}`,
  }));

  return (
    <div
      className="dialog-backdrop"
      onMouseDown={(e) => {
        if (e.target === e.currentTarget && !saving) onClose();
      }}>
      <form
        className="modal-panel experiment-create-modal"
        role="dialog"
        aria-modal="true"
        onSubmit={submit}>
        <div className="modal-header">
          <div>
            <div className="modal-title">New experiment</div>
            <div className="modal-subtitle">
              Run the same workflows and paths against a baseline and a variant.
              {totalRuns > 0 && ` ${totalRuns} runs will be queued.`}
            </div>
          </div>
          <button className="icon-btn" type="button" aria-label="Close" disabled={saving} onClick={onClose}>
            <Icon name="x" size={15} />
          </button>
        </div>
        <div className="modal-body">
          <section className="mcp-form-section">
            <h4 className="mcp-form-section-title">Name &amp; hypothesis</h4>
            <input
              className="input"
              placeholder="e.g. Stage-name enum on update_deal_stage"
              value={name}
              disabled={saving}
              onChange={(e) => setName(e.target.value)} />
            <textarea
              className="input experiment-hypothesis-input"
              placeholder="What do you expect this change to improve? (optional)"
              value={hypothesis}
              rows={2}
              disabled={saving}
              onChange={(e) => setHypothesis(e.target.value)} />
          </section>

          {mcpAnalyticsEnabled && attachableChanges.length > 0 && (
            <section className="mcp-form-section">
              <h4 className="mcp-form-section-title">Tool list change (optional)</h4>
              <p className="mcp-form-section-desc">
                Link the staged change this experiment validates — it moves to
                "in experiment" on dispatch and closes as rejected if you reject the variant.
              </p>
              <Select
                ariaLabel="Tool list change"
                placeholder="No tool list change"
                value={toolListChangeId}
                disabled={saving}
                options={[
                  { value: '', label: 'No tool list change' },
                  ...attachableChanges.map((change) => ({
                    value: change.id,
                    label: `${change.title}${change.server_name ? ` · ${change.server_name}` : ''}`,
                  })),
                ]}
                onChange={pickToolListChange} />
            </section>
          )}

          <section className="mcp-form-section">
            <h4 className="mcp-form-section-title">Baseline vs variant</h4>
            <p className="mcp-form-section-desc">
              Pick the current server as baseline and the server carrying your change as variant.
            </p>
            <div className="experiment-arm-pickers">
              <div className="experiment-arm-picker">
                <div className="text-xs muted">Baseline</div>
                <Select
                  ariaLabel="Baseline MCP server"
                  placeholder="Pick a baseline server"
                  value={baselineId}
                  disabled={saving}
                  options={sourceOptions.filter((opt) => opt.value !== variantId)}
                  onChange={setBaselineId} />
              </div>
              <div className="experiment-arm-picker">
                <div className="text-xs muted">Variant</div>
                <Select
                  ariaLabel="Variant MCP server"
                  placeholder="Pick a variant server"
                  value={variantId}
                  disabled={saving}
                  options={sourceOptions.filter((opt) => opt.value !== baselineId)}
                  onChange={setVariantId} />
              </div>
            </div>
          </section>

          <section className="mcp-form-section">
            <h4 className="mcp-form-section-title">
              Workflow set ({workflowIds.length} selected)
            </h4>
            <p className="mcp-form-section-desc">
              Every selected workflow runs on both arms, once per tester target.
            </p>
            <div className="experiment-workflow-list">
              {workflows.loading && !workflows.data ? (
                <div className="code inline-loading">
                  <LoadingSpinner size="sm" label="Loading workflows" decorative />
                  Loading workflows...
                </div>
              ) : workflowRows.length === 0 ? (
                <EmptyState
                  title="No workflows yet"
                  body="Create a workflow first — experiments replay your workflow set on both arms." />
              ) : workflowRows.map((row) => {
                const checked = workflowIds.includes(row.id);
                return (
                  <label key={row.id} className={`benchmark-pick-row${checked ? ' is-checked' : ''}`}>
                    <input
                      type="checkbox"
                      checked={checked}
                      disabled={saving}
                      onChange={() => toggleWorkflow(row.id)} />
                    <Icon name="workflow" size={14} className="benchmark-pick-row__icon" />
                    <div className="benchmark-pick-row__body">
                      <div className="benchmark-pick-row__name">{row.name}</div>
                    </div>
                  </label>
                );
              })}
            </div>
          </section>

          <section className="mcp-form-section">
            <h4 className="mcp-form-section-title">
              Tester targets ({testerTargets.length} selected)
            </h4>
            <p className="mcp-form-section-desc">
              Each (workflow × target) pair runs once per arm.
            </p>
            <WorkflowTargetPicker
              models={models}
              targetRows={testerTargets}
              modelIds={testerTargets.map((t) => t.modelId)}
              disabled={saving}
              targetLimit={targetLimit}
              portalMenus
              onChange={() => {}}
              onChangeTargets={(rows) => setTesterTargets(rows.map((row) => ({
                harness: row.harness,
                modelId: row.modelId,
              })))} />
          </section>
        </div>
        <div className="modal-actions">
          <Button disabled={saving} onClick={onClose}>Cancel</Button>
          <Button
            variant="primary"
            type="submit"
            disabled={formInvalid}
            loading={saving}
            loadingLabel="Dispatching...">
            <Icon name="play" size={12} />
            Run {totalRuns > 0 ? `${totalRuns} runs` : 'experiment'}
          </Button>
        </div>
      </form>
    </div>
  );
}

// ---------------------------------------------------------------------------
// List page
// ---------------------------------------------------------------------------

function ExperimentsPage({ navigate, recordId = null, queryString = '' }) {
  const auth = useAuth();
  const canCreate = canUseTenantAction(auth, 'editor');
  const list = useApiResource('/api/experiments');
  // ?new=1&tlc=<id> deep-links the create dialog with a tool-list change
  // preselected — the "Launch experiment" action on the Tool registry's
  // proposed-change cards lands here.
  const query = useMemoX(() => new URLSearchParams(queryString || ''), [queryString]);
  const launchTlcId = query.get('tlc') || null;
  const [createOpen, setCreateOpen] = useStateX(() => query.get('new') === '1' || Boolean(launchTlcId));
  const rows = list.data?.rows || [];

  function closeCreate() {
    setCreateOpen(false);
    // Drop the ?new/&tlc params so a refresh doesn't reopen the dialog.
    if (query.get('new') || launchTlcId) navigate('/experiments', { replace: true });
  }

  const runningCount = rows.filter((row) => row.effective_status === 'running').length;

  return (
    <div className="page-inner experiments-page">
      <div className="ui-section-head">
        <div>
          <h2 className="ui-section-title">Experiments</h2>
          <p className="text-xs muted">
            Did the change improve the MCP? Run a variant against baseline before you ship.
          </p>
        </div>
        <div className="ui-page-actions">
          <Button
            variant="primary"
            disabled={!canCreate}
            title={!canCreate ? tenantActionDisabledReason(auth, 'editor') : undefined}
            onClick={() => setCreateOpen(true)}>
            <Icon name="plus" size={13} />
            New experiment
          </Button>
        </div>
      </div>

      {list.loading && !list.data ? (
        <div className="code inline-loading">
          <LoadingSpinner size="sm" label="Loading experiments" decorative />
          Loading experiments...
        </div>
      ) : list.error ? (
        <EmptyState title="Could not load experiments" body={list.error.message} />
      ) : rows.length === 0 ? (
        <div className="card benchmark-empty-card">
          <EmptyState
            title="No experiments yet"
            body="An experiment runs your workflow set across the same harness × model paths on a baseline and a variant MCP server, then compares pass rate, quality, and efficiency."
            action={canCreate ? (
              <Button variant="primary" onClick={() => setCreateOpen(true)}>
                <Icon name="plus" size={13} />
                New experiment
              </Button>
            ) : null} />
        </div>
      ) : (
        <div className="card benchmark-table-wrap">
          <table className="ui-table experiments-table">
            <thead>
              <tr>
                <th>Experiment</th>
                <th>Status</th>
                <th className="num">Workflows</th>
                <th className="num">Paths</th>
                <th className="num">Runs</th>
                <th className="num">Baseline</th>
                <th className="num">Variant</th>
                <th className="num">Δ pass</th>
              </tr>
            </thead>
            <tbody>
              {rows.map((row) => (
                <tr
                  key={row.id}
                  className="ui-table-row-clickable"
                  onClick={() => navigate(`/experiments/${row.id}`)}>
                  <td className="experiment-name-cell">
                    <div>{row.name}</div>
                    {row.hypothesis && (
                      <div className="text-xs muted experiment-name-sub">{row.hypothesis}</div>
                    )}
                  </td>
                  <td><ExperimentStatusPill experiment={row} /></td>
                  <td className="num mono">{(row.workflow_ids || []).length}</td>
                  <td className="num mono">{(row.tester_targets || []).length}</td>
                  <td className="num mono">{row.terminal_runs}/{row.total_runs}</td>
                  <td className="num mono muted">{expFmtPct(row.baseline_pass_rate)}</td>
                  <td className="num mono">{expFmtPct(row.variant_pass_rate)}</td>
                  <td className="num"><ExperimentDelta value={row.pass_rate_delta} /></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {runningCount > 0 && (
        <p className="text-xs muted">
          {runningCount} experiment{runningCount === 1 ? '' : 's'} running — open one to watch results fill in.
        </p>
      )}

      {createOpen && (
        <NewExperimentDialog
          initialToolListChangeId={launchTlcId}
          onClose={closeCreate}
          onCreated={(experiment) => {
            setCreateOpen(false);
            list.reload();
            // Replace the history entry when we arrived via the ?new/&tlc
            // deep link — otherwise Back restores the deep-link URL and
            // reopens the creation dialog. Flagged by Greptile on #1119.
            navigate(`/experiments/${experiment.id}`, {
              replace: Boolean(query.get('new') || launchTlcId),
            });
          }} />
      )}

      {recordId && (
        <ExperimentDrawer
          experimentId={recordId}
          navigate={navigate}
          onClose={() => navigate('/experiments')}
          onChanged={() => list.reload()} />
      )}
    </div>
  );
}

window.ExperimentsPage = ExperimentsPage;
