// screens-abtest.jsx — A/B 测试控制台
// 接 /api/admin/abtest 列表 + 详情 + 创建/编辑 + start/pause/end

const _NA = window.NebulaAdmin;
const adminFetch  = _NA.adminFetch;
const useAdminPoll = _NA.useAdminPoll;

// ─── helpers ────────────────────────────────────────────────────────────────
function statusChip(st, t) {
  const tx = (zh, en) => (t ? t.tx(zh, en) : zh);
  const map = {
    draft:   { cls: 'outline', label: tx('草稿', 'Draft') },
    running: { cls: 'success', label: tx('进行中', 'Running') },
    paused:  { cls: 'warning', label: tx('已暂停', 'Paused') },
    ended:   { cls: '',        label: tx('已结束', 'Ended') },
  };
  const s = map[st] || { cls: 'outline', label: st || '—' };
  return <span className={'badge ' + s.cls}>{s.label}</span>;
}

function pct(n, digits) {
  if (n == null || Number.isNaN(n)) return '—';
  return (n * 100).toFixed(digits == null ? 2 : digits) + '%';
}

function variantColor(idx) {
  const hues = [264, 145, 35, 0, 195, 305, 80];
  return `oklch(0.62 0.16 ${hues[idx % hues.length]})`;
}

function fmtTs(iso) {
  if (!iso) return '—';
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return iso;
  return d.toLocaleString();
}

// 把后端 stats.by_variant 规整成 array，并标记 winner
function normalizeVariants(exp) {
  const variants = (exp && exp.variants) || [];
  const byV = (exp && exp.stats && exp.stats.by_variant) || {};
  let bestRate = -1, bestKey = null;
  variants.forEach(v => {
    const s = byV[v.key];
    if (s && s.n > 0 && s.rate != null && s.rate > bestRate) {
      bestRate = s.rate;
      bestKey = v.key;
    }
  });
  return variants.map(v => {
    const s = byV[v.key] || { n: 0, conv: 0, rate: 0, ci_lo: 0, ci_hi: 0 };
    return { ...v, stats: s, isWinner: v.key === bestKey };
  });
}

// ─── 主屏 ─────────────────────────────────────────────────────────────────
function ABTestScreen({ t, push }) {
  const [tick, setTick] = React.useState(0);
  const refetch = React.useCallback(() => setTick(x => x + 1), []);
  const [data, err] = useAdminPoll('/api/admin/abtest?_=' + tick, 8000, true);
  const [detailId, setDetailId] = React.useState(null);
  const [showNew, setShowNew] = React.useState(false);
  const [editing, setEditing] = React.useState(null);

  const experiments = (data && data.experiments) || [];
  const totalUsers = experiments.reduce((s, e) => s + ((e.stats && e.stats.total_users) || 0), 0);
  const running = experiments.filter(e => e.status === 'running').length;
  const draft   = experiments.filter(e => e.status === 'draft').length;
  const ended   = experiments.filter(e => e.status === 'ended').length;

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('A/B 测试', 'A/B Tests')}</h1>
          <div className="sub">{t.tx('实验灰度 · 转化率 + 95% CI · 流量分配 · 启停控制', 'Experiments · conversion + 95% CI · traffic split · start/stop')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={refetch}><Icons.refresh /> {t.tx('刷新', 'Refresh')}</button>
          <button className="btn btn-pri btn-sm" onClick={() => setShowNew(true)}><Icons.plus /> {t.tx('新建实验', 'New Experiment')}</button>
        </div>
      </div>

      <div className="row-5">
        <KPI label={t.tx('实验总数', 'Total experiments')}   value={String(experiments.length)} delta={t.tx('全部状态', 'All statuses')}            live={!!data} />
        <KPI label={t.tx('进行中', 'Running')}     value={String(running)}            delta={running ? t.tx('采集中', 'Collecting') : '—'} live={!!data} />
        <KPI label={t.tx('草稿', 'Draft')}       value={String(draft)}              delta={draft ? t.tx('待启动', 'Pending start') : '—'}   live={!!data} />
        <KPI label={t.tx('已结束', 'Ended')}     value={String(ended)}              delta={t.tx('归档', 'Archived')}                   live={!!data} />
        <KPI label={t.tx('累计参与', 'Cumulative users')}   value={fmtNum(totalUsers)}         delta={t.tx('所有 variants 合计', 'All variants combined')}     live={!!data} />
      </div>

      {err && <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败：', 'Load failed: ')}{String(err.message || err)}</div>}
      {!data && !err && <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}

      {data && experiments.length === 0 && (
        <div className="empty">{t.tx('还没有实验 · 点击「新建实验」', 'No experiments yet · click "New Experiment"')}</div>
      )}

      {data && experiments.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(380px, 1fr))', gap: 14 }}>
          {experiments.map(exp => (
            <ExperimentCard
              key={exp.id}
              exp={exp}
              push={push}
              t={t}
              onAction={refetch}
              onDetail={() => setDetailId(exp.id)}
              onEdit={() => setEditing(exp)}
            />
          ))}
        </div>
      )}

      {detailId && (
        <ExperimentDetailModal
          id={detailId}
          onClose={() => setDetailId(null)}
          push={push}
          t={t}
        />
      )}

      {showNew && (
        <ExperimentFormModal
          onClose={() => setShowNew(false)}
          push={push}
          t={t}
          onSaved={() => { setShowNew(false); refetch(); }}
        />
      )}

      {editing && (
        <ExperimentFormModal
          exp={editing}
          onClose={() => setEditing(null)}
          push={push}
          t={t}
          onSaved={() => { setEditing(null); refetch(); }}
        />
      )}
    </div>
  );
}

// ─── 实验卡 ───────────────────────────────────────────────────────────────
function ExperimentCard({ exp, push, t, onAction, onDetail, onEdit }) {
  const [busy, setBusy] = React.useState(false);
  const variants = normalizeVariants(exp);

  const post = async (action) => {
    setBusy(true);
    try {
      await adminFetch(`/api/admin/abtest/${exp.id}/${action}`, { method: 'POST' });
      const labels = {
        start: t.tx('已启动', 'Started'),
        pause: t.tx('已暂停', 'Paused'),
        end: t.tx('已结束', 'Ended'),
        resume: t.tx('已继续', 'Resumed'),
      };
      push((labels[action] || action) + ' ' + exp.id);
      onAction();
    } catch (e) {
      push(t.tx('操作失败：', 'Operation failed: ') + (e.body?.error || e.message));
    } finally {
      setBusy(false);
    }
  };

  const actionButtons = () => {
    if (exp.status === 'draft')   return <button className="btn btn-pri btn-xs" disabled={busy} onClick={() => post('start')}>{t.tx('启动', 'Start')}</button>;
    if (exp.status === 'running') return <>
      <button className="btn btn-xs" disabled={busy} onClick={() => post('pause')}>{t.tx('暂停', 'Pause')}</button>
      <button className="btn btn-xs" disabled={busy} onClick={() => { if (window.confirm(t.tx('结束实验 ', 'End experiment ') + exp.id + '？')) post('end'); }}>{t.tx('结束', 'End')}</button>
    </>;
    if (exp.status === 'paused')  return <>
      <button className="btn btn-pri btn-xs" disabled={busy} onClick={() => post('start')}>{t.tx('继续', 'Resume')}</button>
      <button className="btn btn-xs" disabled={busy} onClick={() => { if (window.confirm(t.tx('结束实验 ', 'End experiment ') + exp.id + '？')) post('end'); }}>{t.tx('结束', 'End')}</button>
    </>;
    if (exp.status === 'ended')   return <span className="text-faint" style={{ fontSize: 11 }}>{t.tx('已结束', 'Ended')}</span>;
    return null;
  };

  const totalUsers = (exp.stats && exp.stats.total_users) || 0;

  return (
    <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
      <div className="card-h" style={{ alignItems: 'flex-start' }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
            <h3 style={{ marginRight: 4 }}>{exp.name || exp.id}</h3>
            {statusChip(exp.status, t)}
            <span className="badge outline" style={{ fontSize: 10 }}>{variants.length} variants</span>
          </div>
          <div className="text-mono text-faint" style={{ fontSize: 11, marginTop: 2 }}>{exp.id}</div>
        </div>
      </div>

      <div style={{ padding: '10px 14px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {variants.map((v, i) => (
          <VariantRow key={v.key} v={v} idx={i} totalUsers={totalUsers} t={t} />
        ))}
        {variants.length === 0 && <div className="text-faint" style={{ fontSize: 11 }}>{t.tx('未配置 variants', 'No variants configured')}</div>}
      </div>

      <div style={{
        padding: '10px 14px', borderTop: '1px solid var(--border)',
        display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap',
        fontSize: 11, color: 'var(--text-muted)',
      }}>
        <span>{t.tx('流量', 'Traffic')} <b style={{ color: 'var(--text)' }}>{exp.traffic_pct != null ? exp.traffic_pct + '%' : '—'}</b></span>
        <span>·</span>
        <span>{t.tx('指标', 'Metric')} <span className="text-mono" style={{ color: 'var(--text)' }}>{exp.primary_metric || '—'}</span></span>
        <div style={{ flex: 1 }} />
        <button className="btn btn-ghost btn-xs" onClick={onDetail}>{t.tx('详情', 'Detail')}</button>
        {exp.status === 'draft' && <button className="btn btn-ghost btn-xs" onClick={onEdit}>{t.tx('编辑', 'Edit')}</button>}
        {actionButtons()}
      </div>
    </div>
  );
}

// ─── variant 横向条形图 + 转化 + CI ────────────────────────────────────────
function VariantRow({ v, idx, totalUsers, t }) {
  const tx = (zh, en) => (t ? t.tx(zh, en) : zh);
  const color = variantColor(idx);
  const s = v.stats;
  const weight = Number(v.weight) || 0;
  const rate = s.rate || 0;
  const ciLo = s.ci_lo || 0;
  const ciHi = s.ci_hi || 0;
  // CI 进度条：[0,1] 区间，rate 是当前点
  const ciLoPct = Math.max(0, Math.min(100, ciLo * 100));
  const ciHiPct = Math.max(0, Math.min(100, ciHi * 100));
  const ratePct = Math.max(0, Math.min(100, rate * 100));

  const winnerStyle = v.isWinner ? {
    boxShadow: '0 0 0 1.5px var(--gold, oklch(0.78 0.13 85))',
    background: 'oklch(0.78 0.13 85 / 0.05)',
  } : {};

  return (
    <div style={{
      border: '1px solid var(--border)',
      borderLeft: `3px solid ${color}`,
      borderRadius: 7, padding: '8px 10px',
      ...winnerStyle,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
        <span style={{ fontWeight: 600, fontSize: 12.5 }}>{v.key}</span>
        {v.isWinner && <span className="badge gold" style={{ fontSize: 10 }}>winner</span>}
        <div style={{ flex: 1 }} />
        <span className="text-faint" style={{ fontSize: 11 }}>weight {weight}%</span>
      </div>

      {/* 权重条 vs 实际样本 share */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
        <span className="text-faint" style={{ fontSize: 10, width: 30 }}>{tx('分配', 'Split')}</span>
        <span className="barmini" style={{ flex: 1, height: 6 }}>
          <i style={{ width: weight + '%', background: color }} />
        </span>
        <span className="tabular text-muted" style={{ fontSize: 11, width: 64, textAlign: 'right' }}>
          {fmtNum(s.n)} {tx('样本', 'samples')}
        </span>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11.5 }}>
        <span className="text-muted">{tx('转化', 'Conv.')}</span>
        <span className="tabular" style={{ fontWeight: 600, color: v.isWinner ? 'var(--text)' : 'var(--text)' }}>
          {pct(rate)}
        </span>
        <span className="text-faint" style={{ fontSize: 10.5 }}>
          ({fmtNum(s.conv)} / {fmtNum(s.n)})
        </span>
        <div style={{ flex: 1 }} />
        <span className="text-faint" style={{ fontSize: 10.5 }}>
          95% CI [{pct(ciLo, 1)}, {pct(ciHi, 1)}]
        </span>
      </div>

      {/* CI 区间可视化 */}
      <div style={{ position: 'relative', height: 6, marginTop: 6, background: 'var(--bg-active)', borderRadius: 999 }}>
        <div style={{
          position: 'absolute', top: 0, bottom: 0,
          left: ciLoPct + '%', width: Math.max(0.5, ciHiPct - ciLoPct) + '%',
          background: color, opacity: 0.35, borderRadius: 999,
        }} />
        <div style={{
          position: 'absolute', top: -1, bottom: -1,
          left: `calc(${ratePct}% - 1px)`, width: 2,
          background: color, borderRadius: 1,
        }} />
      </div>
    </div>
  );
}

// ─── 详情 Modal ───────────────────────────────────────────────────────────
function ExperimentDetailModal({ id, onClose, push, t }) {
  const [exp, setExp] = React.useState(null);
  const [err, setErr] = React.useState(null);

  React.useEffect(() => {
    let stopped = false;
    (async () => {
      try {
        const d = await adminFetch(`/api/admin/abtest/${encodeURIComponent(id)}`);
        if (!stopped) setExp(d.experiment || d);
      } catch (e) {
        if (!stopped) setErr(e);
        push(t.tx('详情加载失败: ', 'Failed to load detail: ') + (e.body?.error || e.message));
      }
    })();
    return () => { stopped = true; };
  }, [id, push, t]);

  const variants = exp ? normalizeVariants(exp) : [];

  return (
    <Modal open={true} onClose={onClose} title={t.tx('实验 · ', 'Experiment · ') + (exp ? (exp.name || exp.id) : id)} width={680}
           footer={<button className="btn btn-sm" onClick={onClose}>{t.tx('关闭', 'Close')}</button>}>
      {!exp && !err && <div className="text-muted" style={{ padding: 16, textAlign: 'center' }}>{t.tx('加载中…', 'Loading…')}</div>}
      {err && <div style={{ color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败', 'Load failed')}</div>}
      {exp && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14, fontSize: 12 }}>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
            <span className="text-mono text-faint">{exp.id}</span>
            {statusChip(exp.status, t)}
            <span className="text-faint">·</span>
            <span className="text-muted">{t.tx('流量', 'Traffic')} {exp.traffic_pct != null ? exp.traffic_pct + '%' : '—'}</span>
            <span className="text-faint">·</span>
            <span className="text-muted">{t.tx('主指标', 'Primary metric')} <span className="text-mono" style={{ color: 'var(--text)' }}>{exp.primary_metric || '—'}</span></span>
          </div>

          {exp.description && (
            <div style={{ padding: 10, background: 'var(--bg-inset)', borderRadius: 6, fontSize: 12, lineHeight: 1.5 }}>
              {exp.description}
            </div>
          )}

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            <div><span className="text-muted">{t.tx('开始', 'Start')}</span> <span className="text-mono">{fmtTs(exp.start_at)}</span></div>
            <div><span className="text-muted">{t.tx('结束', 'End')}</span> <span className="text-mono">{fmtTs(exp.end_at)}</span></div>
            <div><span className="text-muted">{t.tx('累计用户', 'Cumulative users')}</span> <span className="tabular">{fmtNum((exp.stats && exp.stats.total_users) || 0)}</span></div>
            <div><span className="text-muted">variants</span> <span>{variants.length}</span></div>
          </div>

          <div>
            <div className="text-muted" style={{ fontSize: 11, marginBottom: 6 }}>{t.tx('variants 表现', 'Variant performance')}</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {variants.map((v, i) => {
                // 简单 sparkline：把 n 拆成 8 段的累计形状（伪时间序列）
                const n = v.stats.n || 0;
                const fake = Array.from({ length: 8 }, (_, k) => Math.round(n * ((k + 1) / 8) * (0.85 + Math.sin(i + k) * 0.07)));
                return (
                  <div key={v.key} style={{
                    border: '1px solid var(--border)',
                    borderLeft: `3px solid ${variantColor(i)}`,
                    borderRadius: 7, padding: 10,
                    ...(v.isWinner ? { boxShadow: '0 0 0 1.5px var(--gold, oklch(0.78 0.13 85))' } : {}),
                  }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
                      <span style={{ fontWeight: 600 }}>{v.key}</span>
                      {v.isWinner && <span className="badge gold" style={{ fontSize: 10 }}>winner</span>}
                      <span className="text-faint" style={{ fontSize: 11 }}>weight {v.weight}%</span>
                      <div style={{ flex: 1 }} />
                      <Sparkline data={fake.length ? fake : [0, 0]} w={120} h={28} stroke={variantColor(i)} />
                    </div>
                    <div style={{ display: 'flex', gap: 12, fontSize: 11.5 }}>
                      <span><span className="text-muted">n</span> <span className="tabular">{fmtNum(v.stats.n)}</span></span>
                      <span><span className="text-muted">conv</span> <span className="tabular">{fmtNum(v.stats.conv)}</span></span>
                      <span><span className="text-muted">rate</span> <span className="tabular" style={{ fontWeight: 600 }}>{pct(v.stats.rate)}</span></span>
                      <span className="text-faint">CI [{pct(v.stats.ci_lo, 1)}, {pct(v.stats.ci_hi, 1)}]</span>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </Modal>
  );
}

// ─── 新建/编辑 Modal ──────────────────────────────────────────────────────
function ExperimentFormModal({ exp, onClose, push, onSaved, t }) {
  const isEdit = !!exp;
  const [form, setForm] = React.useState(() => ({
    id: exp?.id || '',
    name: exp?.name || '',
    description: exp?.description || '',
    traffic_pct: exp?.traffic_pct != null ? exp.traffic_pct : 100,
    primary_metric: exp?.primary_metric || '',
    start_at: toLocalInput(exp?.start_at),
    end_at: toLocalInput(exp?.end_at),
    variants: (exp?.variants && exp.variants.length)
      ? exp.variants.map(v => ({ key: v.key, weight: v.weight }))
      : [{ key: 'control', weight: 50 }, { key: 'treatment', weight: 50 }],
  }));
  const [busy, setBusy] = React.useState(false);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const setVariant = (i, k, v) => setForm(f => {
    const arr = f.variants.slice();
    arr[i] = { ...arr[i], [k]: v };
    return { ...f, variants: arr };
  });
  const addVariant = () => setForm(f => ({ ...f, variants: [...f.variants, { key: 'v' + (f.variants.length + 1), weight: 0 }] }));
  const removeVariant = (i) => setForm(f => ({ ...f, variants: f.variants.filter((_, idx) => idx !== i) }));

  const weightSum = form.variants.reduce((s, v) => s + (Number(v.weight) || 0), 0);
  const sumOk = Math.abs(weightSum - 100) < 0.001;
  const idOk = isEdit || !!form.id.trim();
  const keysOk = form.variants.length >= 2 && form.variants.every(v => !!v.key.trim());
  const keysUnique = new Set(form.variants.map(v => v.key.trim())).size === form.variants.length;
  const canSave = idOk && sumOk && keysOk && keysUnique;

  const submit = async () => {
    if (!canSave) {
      if (!idOk) return push(t.tx('请填写 id', 'Please enter id'));
      if (!keysOk) return push(t.tx('每个 variant 都需要 key', 'Each variant needs a key'));
      if (!keysUnique) return push(t.tx('variant key 不能重复', 'Variant keys must be unique'));
      if (!sumOk) return push(t.tx('权重之和必须 = 100，当前 ', 'Weight sum must = 100, current ') + weightSum);
      return;
    }
    setBusy(true);
    try {
      const body = {
        id: form.id.trim(),
        name: form.name.trim(),
        description: form.description,
        traffic_pct: Number(form.traffic_pct),
        primary_metric: form.primary_metric.trim(),
        variants: form.variants.map(v => ({ key: v.key.trim(), weight: Number(v.weight) })),
        start_at: form.start_at ? new Date(form.start_at).toISOString() : null,
        end_at:   form.end_at   ? new Date(form.end_at).toISOString()   : null,
      };
      if (isEdit) {
        await adminFetch(`/api/admin/abtest/${encodeURIComponent(exp.id)}`, {
          method: 'PUT', body: JSON.stringify(body),
        });
        push(t.tx('已更新 ', 'Updated ') + exp.id);
      } else {
        await adminFetch('/api/admin/abtest', {
          method: 'POST', body: JSON.stringify(body),
        });
        push(t.tx('已创建 ', 'Created ') + body.id);
      }
      onSaved();
    } catch (e) {
      push((isEdit ? t.tx('保存失败：', 'Save failed: ') : t.tx('创建失败：', 'Create failed: ')) + (e.body?.error || e.message));
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open={true} onClose={onClose} title={isEdit ? t.tx('编辑实验 · ', 'Edit Experiment · ') + exp.id : t.tx('新建实验', 'New Experiment')} width={620}
           footer={<>
             <button className="btn btn-sm" onClick={onClose}>{t.tx('取消', 'Cancel')}</button>
             <button className="btn btn-pri btn-sm" disabled={busy || !canSave} onClick={submit}>
               {busy ? t.tx('提交中…', 'Submitting…') : (isEdit ? t.tx('保存', 'Save') : t.tx('创建', 'Create'))}
             </button>
           </>}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <div className="field">
          <label>{t.tx('实验 ID', 'Experiment ID')} {!isEdit && <span style={{ color: 'var(--danger)' }}>*</span>}</label>
          <input className="input" disabled={isEdit} value={form.id}
                 onChange={e => set('id', e.target.value)} placeholder={t.tx('如 home-cta-color', 'e.g. home-cta-color')} />
        </div>
        <div className="field">
          <label>{t.tx('名称', 'Name')}</label>
          <input className="input" value={form.name} onChange={e => set('name', e.target.value)} placeholder={t.tx('首页按钮颜色测试', 'Home CTA color test')} />
        </div>
        <div className="field" style={{ gridColumn: '1 / -1' }}>
          <label>{t.tx('描述', 'Description')}</label>
          <textarea className="input" rows={2} value={form.description}
                    onChange={e => set('description', e.target.value)} placeholder={t.tx('假设：橙色 CTA 提升首充转化…', 'Hypothesis: orange CTA lifts first-recharge conversion…')} />
        </div>
        <div className="field">
          <label>{t.tx('主指标', 'Primary metric')}</label>
          <input className="input" value={form.primary_metric}
                 onChange={e => set('primary_metric', e.target.value)} placeholder="first_recharge / signup_complete" />
        </div>
        <div className="field">
          <label>{t.tx('流量比例', 'Traffic %')} <span className="text-faint">{form.traffic_pct}%</span></label>
          <input type="range" min="0" max="100" step="1" value={form.traffic_pct}
                 onChange={e => set('traffic_pct', Number(e.target.value))}
                 style={{ width: '100%' }} />
        </div>
        <div className="field">
          <label>{t.tx('开始时间（可选）', 'Start time (optional)')}</label>
          <input className="input" type="datetime-local" value={form.start_at}
                 onChange={e => set('start_at', e.target.value)} />
        </div>
        <div className="field">
          <label>{t.tx('结束时间（可选）', 'End time (optional)')}</label>
          <input className="input" type="datetime-local" value={form.end_at}
                 onChange={e => set('end_at', e.target.value)} />
        </div>
      </div>

      <div style={{ marginTop: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
          <span style={{ fontSize: 12, fontWeight: 600 }}>variants</span>
          <span className={'badge ' + (sumOk ? 'success' : 'danger')} style={{ fontSize: 10 }}>
            {t.tx('权重和 ', 'Weight sum ')}{weightSum}{sumOk ? ' ✓' : ' / 100'}
          </span>
          <div style={{ flex: 1 }} />
          <button className="btn btn-xs" onClick={addVariant}><Icons.plus /> {t.tx('添加', 'Add')}</button>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {form.variants.map((v, i) => (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '20px 1fr 110px 28px', gap: 8, alignItems: 'center',
              border: '1px solid var(--border)', borderLeft: `3px solid ${variantColor(i)}`,
              borderRadius: 6, padding: '6px 8px',
            }}>
              <span className="text-faint text-mono" style={{ fontSize: 11 }}>#{i + 1}</span>
              <input className="input" placeholder={t.tx('key, 如 control / orange_cta', 'key, e.g. control / orange_cta')}
                     value={v.key} onChange={e => setVariant(i, 'key', e.target.value)} />
              <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                <input className="input" type="number" min="0" max="100" step="1"
                       value={v.weight} onChange={e => setVariant(i, 'weight', e.target.value)}
                       style={{ width: 70 }} />
                <span className="text-faint" style={{ fontSize: 11 }}>%</span>
              </div>
              <button className="icon-btn" disabled={form.variants.length <= 2}
                      onClick={() => removeVariant(i)} title={t.tx('移除', 'Remove')}><Icons.x /></button>
            </div>
          ))}
        </div>
        {!keysUnique && <div style={{ marginTop: 6, fontSize: 11, color: 'var(--danger)' }}>{t.tx('variant key 不能重复', 'Variant keys must be unique')}</div>}
      </div>
    </Modal>
  );
}

function toLocalInput(iso) {
  if (!iso) return '';
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return '';
  const pad = n => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

Object.assign(window, { ABTestScreen });
