// screens-templates.jsx — 装修模板库（节日皮肤 / 新人专题 / 高额返水 等）
// 列表 + 分类 chip + 卡片网格 + 一键应用到 home 草稿 + 从当前草稿快照创建

const _TPL = window.NebulaAdmin;
const _tplFetch = _TPL.adminFetch;
const _tplPoll  = _TPL.useAdminPoll;

// ─── helpers ────────────────────────────────────────────────────────────────
const _TPL_CAT_COLORS = { '': 'outline', general: 'outline', festival: 'accent', activity: 'success', newbie: 'info', brand: 'purple' };
const _TPL_CATEGORIES = (t) => {
  const tx = (t && t.tx) ? t.tx : ((zh) => zh);
  return [
    { id: '',         label: tx('全部', 'All'),      color: 'outline' },
    { id: 'general',  label: tx('通用', 'General'),  color: 'outline' },
    { id: 'festival', label: tx('节日', 'Festival'), color: 'accent'  },
    { id: 'activity', label: tx('活动', 'Activity'), color: 'success' },
    { id: 'newbie',   label: tx('新人', 'Newbie'),   color: 'info'    },
    { id: 'brand',    label: tx('品牌', 'Brand'),    color: 'purple'  },
  ];
};

function _tplCatColor(cat) {
  return _TPL_CAT_COLORS[cat] || 'outline';
}

function _tplCatLabel(cat, t) {
  const m = _TPL_CATEGORIES(t).find(c => c.id === cat);
  return m ? m.label : cat;
}

// ─── KPI cell ───────────────────────────────────────────────────────────────
function _TplStatCell({ label, value, color }) {
  return (
    <div>
      <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>{label}</div>
      <div className="tabular" style={{ fontSize: 18, fontWeight: 700, color: color || 'var(--text)' }}>{value}</div>
    </div>
  );
}

// ─── Field helper ───────────────────────────────────────────────────────────
function _TplField({ label, hint, children }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <label style={{ fontSize: 11, color: 'var(--text-muted)' }}>{label}</label>
      {children}
      {hint && <div className="text-faint" style={{ fontSize: 10 }}>{hint}</div>}
    </div>
  );
}

// ─── 主屏 ───────────────────────────────────────────────────────────────────
function TemplatesScreen({ t, push }) {
  push = push || ((m) => console.log('[templates]', m));
  const [catFilter, setCatFilter] = React.useState('');
  const [pageFilter, setPageFilter] = React.useState('');
  const [tick, setTick] = React.useState(0);
  const bump = () => setTick(v => v + 1);

  const [showNew, setShowNew] = React.useState(false);
  const [editing, setEditing] = React.useState(null);

  const listPath = React.useMemo(() => {
    const p = new URLSearchParams();
    if (catFilter) p.set('category', catFilter);
    if (pageFilter) p.set('page', pageFilter);
    p.set('_', String(tick));
    return '/api/templates?' + p.toString();
  }, [catFilter, pageFilter, tick]);

  const [listData, listErr] = _tplPoll(listPath, 15000);
  const items = (listData && listData.templates) || [];

  // KPI：总数 + 各分类数（不受 filter 影响 → 单独跑一份 query）
  const [allData] = _tplPoll('/api/templates?_=' + tick, 30000);
  const allItems = (allData && allData.templates) || [];
  const kpi = React.useMemo(() => {
    const r = { total: allItems.length, general: 0, festival: 0, activity: 0, newbie: 0, brand: 0 };
    for (const a of allItems) {
      if (a.category in r) r[a.category]++;
    }
    return r;
  }, [allItems]);

  const refresh = () => setTimeout(bump, 600);

  const doApply = async (tpl, merge) => {
    const verbZh = merge === 'append' ? '追加' : '覆盖';
    const verbEn = merge === 'append' ? 'append' : 'overwrite';
    if (!confirm(t.tx(`将模板「${tpl.name}」${verbZh}应用到 ${tpl.target_page} 草稿？`, `${verbEn === 'append' ? 'Append' : 'Overwrite'} template "${tpl.name}" to ${tpl.target_page} draft?`))) return;
    try {
      const res = await _tplFetch(`/api/admin/templates/${tpl.id}/apply`, {
        method: 'POST',
        body: JSON.stringify({ page: tpl.target_page, merge }),
      });
      push(t.tx(`已${verbZh}到 ${res.page} · v${res.version} · ${res.comp_count} 个组件`, `${verbEn === 'append' ? 'Appended' : 'Overwritten'} to ${res.page} · v${res.version} · ${res.comp_count} components`));
      refresh();
    } catch (e) {
      push(t.tx('应用失败: ', 'Apply failed: ') + (e.body?.error || e.message || e));
    }
  };

  const doDelete = async (tpl) => {
    if (!confirm(t.tx(`删除模板「${tpl.name}」？（软删 active=0）`, `Delete template "${tpl.name}"? (soft delete active=0)`))) return;
    try {
      await _tplFetch(`/api/admin/templates/${tpl.id}`, { method: 'DELETE' });
      push(t.tx('已删除', 'Deleted'));
      refresh();
    } catch (e) {
      push(t.tx('删除失败: ', 'Delete failed: ') + (e.body?.error || e.message || e));
    }
  };

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('装修模板库', 'Template Library')}</h1>
          <div className="sub">{t.tx('节日皮肤 / 新人专题 / 高额返水 — 一键应用到任意页面草稿', 'Festival skins / newbie campaigns / high rebates — one-click apply to any page draft')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={bump}>{t.tx('刷新', 'Refresh')}</button>
          <button className="btn btn-pri btn-sm" onClick={() => setShowNew(true)}>{t.tx('+ 创建模板', '+ New Template')}</button>
        </div>
      </div>

      {/* KPI strip */}
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 18, flexWrap: 'wrap', alignItems: 'center' }}>
          <_TplStatCell label={t.tx('模板总数', 'Total templates')} value={kpi.total} />
          <div className="divider-v" style={{ height: 30 }} />
          <_TplStatCell label={t.tx('通用 general', 'General')} value={kpi.general} />
          <div className="divider-v" style={{ height: 30 }} />
          <_TplStatCell label={t.tx('节日 festival', 'Festival')} value={kpi.festival} color="var(--accent-text)" />
          <div className="divider-v" style={{ height: 30 }} />
          <_TplStatCell label={t.tx('活动 activity', 'Activity')} value={kpi.activity} color="var(--success)" />
          <div className="divider-v" style={{ height: 30 }} />
          <_TplStatCell label={t.tx('新人 newbie', 'Newbie')} value={kpi.newbie} color="var(--info, #0ea5e9)" />
          <div className="divider-v" style={{ height: 30 }} />
          <_TplStatCell label={t.tx('品牌 brand', 'Brand')} value={kpi.brand} />
        </div>
      </div>

      {/* filter chips */}
      <div className="card" style={{ padding: 12 }}>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center', marginBottom: 6 }}>
          <span className="text-faint" style={{ fontSize: 10, width: 50 }}>{t.tx('分类', 'Category')}</span>
          {_TPL_CATEGORIES(t).map(c => (
            <button key={'c' + c.id}
                    className={'chip ' + (catFilter === c.id ? 'active' : '')}
                    style={{ fontSize: 10 }}
                    onClick={() => setCatFilter(c.id)}>
              {c.label}
            </button>
          ))}
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
          <span className="text-faint" style={{ fontSize: 10, width: 50 }}>{t.tx('目标页', 'Target page')}</span>
          {['', 'home', 'promos', 'me'].map(p => (
            <button key={'p' + p}
                    className={'chip ' + (pageFilter === p ? 'active' : '')}
                    style={{ fontSize: 10 }}
                    onClick={() => setPageFilter(p)}>
              {p === '' ? t.tx('全部', 'All') : p}
            </button>
          ))}
        </div>
      </div>

      {listErr && (
        <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>
          {t.tx('加载失败: ', 'Load failed: ')}{String(listErr.message || listErr)}
        </div>
      )}

      {!listData && !listErr && (
        <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>
          {t.tx('加载中…', 'Loading…')}
        </div>
      )}

      {listData && items.length === 0 && (
        <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-faint)', fontSize: 12 }}>
          {t.tx('没有符合条件的模板', 'No templates match')}
        </div>
      )}

      {/* 卡片网格 */}
      {listData && items.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 14 }}>
          {items.map(tpl => (
            <TemplateCard key={tpl.id}
                          tpl={tpl}
                          t={t}
                          onApply={(merge) => doApply(tpl, merge)}
                          onEdit={() => setEditing(tpl)}
                          onDelete={() => doDelete(tpl)} />
          ))}
        </div>
      )}

      {showNew && (
        <TemplateCreateModal
          onClose={() => setShowNew(false)}
          onSaved={() => { setShowNew(false); refresh(); }}
          push={push}
          t={t}
        />
      )}
      {editing && (
        <TemplateEditModal
          tpl={editing}
          onClose={() => setEditing(null)}
          onSaved={() => { setEditing(null); refresh(); }}
          push={push}
          t={t}
        />
      )}
    </div>
  );
}

// ─── Card ──────────────────────────────────────────────────────────────────
function TemplateCard({ tpl, t, onApply, onEdit, onDelete }) {
  return (
    <div className="card" style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* preview area */}
      <div style={{
        height: 130,
        background: tpl.preview_image_url
          ? `center / cover no-repeat url(${tpl.preview_image_url})`
          : 'linear-gradient(135deg, var(--bg-inset), var(--bg-card))',
        borderBottom: '1px solid var(--border)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--text-faint)', fontSize: 11,
      }}>
        {!tpl.preview_image_url && <span>· no preview ·</span>}
      </div>
      <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 8, flex: 1 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={tpl.name}>
              {tpl.name}
            </div>
            <div className="mono text-faint" style={{ fontSize: 10, marginTop: 2 }}>{tpl.id}</div>
          </div>
          <span className={'chip ' + _tplCatColor(tpl.category)} style={{ fontSize: 10 }}>
            {_tplCatLabel(tpl.category, t)}
          </span>
        </div>

        <div className="text-muted" style={{ fontSize: 11, lineHeight: 1.4, minHeight: 30 }}>
          {tpl.description || <span className="text-faint">{t.tx('无描述', 'No description')}</span>}
        </div>

        {(tpl.tags && tpl.tags.length > 0) && (
          <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
            {tpl.tags.map(tag => (
              <span key={tag} className="chip outline" style={{ fontSize: 9 }}>{tag}</span>
            ))}
          </div>
        )}

        <div style={{ display: 'flex', gap: 12, fontSize: 10, color: 'var(--text-faint)', marginTop: 'auto' }}>
          <span>{t.tx('目标:', 'Target:')} <b style={{ color: 'var(--text-muted)' }}>{tpl.target_page}</b></span>
          <span>{t.tx('用量:', 'Usage:')} <b style={{ color: 'var(--text-muted)' }}>{tpl.usage_count || 0}</b></span>
        </div>

        <div style={{ display: 'flex', gap: 6, marginTop: 4, flexWrap: 'wrap' }}>
          <button className="btn btn-pri btn-sm" style={{ flex: 1, minWidth: 80 }} onClick={() => onApply('replace')}>
            {t.tx('应用到 ', 'Apply to ')}{tpl.target_page}
          </button>
          <button className="btn btn-sm" onClick={() => onApply('append')} title={t.tx('追加到现有 components 尾部', 'Append to end of existing components')}>{t.tx('追加', 'Append')}</button>
          <button className="btn btn-sm" onClick={onEdit}>{t.tx('编辑', 'Edit')}</button>
          <button className="btn btn-sm btn-danger" onClick={onDelete}>{t.tx('删除', 'Delete')}</button>
        </div>
      </div>
    </div>
  );
}

// ─── 创建 modal：从当前 home 草稿快照 ─────────────────────────────────────
function TemplateCreateModal({ onClose, onSaved, push, t }) {
  const [form, setForm] = React.useState({
    id:           '',
    name:         '',
    description:  '',
    category:     'general',
    target_page:  'home',
    tags:         '',
    preview_image_url: '',
  });
  const [sourcePage, setSourcePage] = React.useState('home');
  const [snapshot, setSnapshot] = React.useState(null);
  const [snapErr, setSnapErr] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const loadSnapshot = async () => {
    setSnapshot(null); setSnapErr(null);
    try {
      const d = await _tplFetch(`/api/layouts/${sourcePage}/draft`);
      setSnapshot(d);
    } catch (e) {
      setSnapErr(e);
    }
  };

  React.useEffect(() => { loadSnapshot(); /* eslint-disable-next-line */ }, [sourcePage]);

  const submit = async () => {
    if (!form.name.trim()) { push(t.tx('名称必填', 'Name is required')); return; }
    if (!snapshot || !Array.isArray(snapshot.components)) {
      push(t.tx('草稿为空，无法快照', 'Draft is empty, cannot snapshot')); return;
    }
    setBusy(true);
    try {
      const payload = {
        id:           form.id.trim() || undefined,
        name:         form.name.trim(),
        description:  form.description,
        category:     form.category,
        target_page:  form.target_page,
        preview_image_url: form.preview_image_url.trim() || undefined,
        tags:         form.tags.split(/[,，]\s*/).map(s => s.trim()).filter(Boolean),
        components:   snapshot.components,
      };
      const r = await _tplFetch('/api/admin/templates', {
        method: 'POST',
        body: JSON.stringify(payload),
      });
      push(t.tx(`已创建模板 ${r.id} · 含 ${snapshot.components.length} 个组件`, `Created template ${r.id} · ${snapshot.components.length} components`));
      onSaved();
    } catch (e) {
      push(t.tx('创建失败: ', 'Create failed: ') + (e.body?.error || e.message || e));
    } finally {
      setBusy(false);
    }
  };

  return (
    <Modal open={true} onClose={onClose} title={t.tx('从当前草稿创建模板', 'Create template from current draft')} width={580}
           footer={<>
             <button className="btn btn-sm" onClick={onClose}>{t.tx('取消', 'Cancel')}</button>
             <button className="btn btn-pri btn-sm" disabled={busy} onClick={submit}>
               {busy ? t.tx('提交中…', 'Submitting…') : t.tx('保存模板', 'Save template')}
             </button>
           </>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <_TplField label={t.tx('快照来源页', 'Snapshot source page')}>
          <div style={{ display: 'flex', gap: 4 }}>
            {['home', 'promos', 'me'].map(p => (
              <button key={p} type="button"
                      className={'chip ' + (sourcePage === p ? 'active' : '')}
                      onClick={() => setSourcePage(p)}>
                {p}
              </button>
            ))}
            <button type="button" className="btn btn-sm" onClick={loadSnapshot}>{t.tx('重新载入', 'Reload')}</button>
          </div>
        </_TplField>
        <div style={{ fontSize: 11, color: 'var(--text-muted)', padding: '6px 10px', background: 'var(--bg-inset)', borderRadius: 6 }}>
          {snapErr && <span style={{ color: 'var(--danger)' }}>{t.tx('草稿加载失败: ', 'Draft load failed: ')}{String(snapErr.message || snapErr)}</span>}
          {!snapErr && !snapshot && <span>{t.tx('读取中…', 'Loading…')}</span>}
          {snapshot && (
            <span>
              {t.tx('将快照 ', 'Snapshot of ')}<b>{sourcePage}</b> · v{snapshot.version || '?'} · {t.tx('共', 'total')}
              <b style={{ color: 'var(--accent-text)' }}> {(snapshot.components || []).length} </b>
              {t.tx('个组件', 'components')}
            </span>
          )}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <_TplField label={t.tx('模板 ID', 'Template ID')} hint={t.tx('留空则自动生成 tpl-xxxxxxxx', 'Empty auto-generates tpl-xxxxxxxx')}>
            <input className="input" value={form.id} onChange={e => set('id', e.target.value)}
                   placeholder={t.tx('例：spring_festival_2027', 'e.g. spring_festival_2027')} />
          </_TplField>
          <_TplField label={t.tx('模板名称 *', 'Template name *')}>
            <input className="input" value={form.name} onChange={e => set('name', e.target.value)}
                   placeholder={t.tx('例：春节红包雨', 'e.g. Spring Festival Red Packet Rain')} />
          </_TplField>
        </div>

        <_TplField label={t.tx('描述', 'Description')}>
          <textarea className="textarea" rows={2}
                    value={form.description} onChange={e => set('description', e.target.value)}
                    placeholder={t.tx('一句话说明用途，比如：春节假期红底金字 + 红包套餐秒杀', 'One line description, e.g. Spring Festival red & gold theme + red packet flash sale')} />
        </_TplField>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
          <_TplField label={t.tx('分类', 'Category')}>
            <select className="select" value={form.category} onChange={e => set('category', e.target.value)}>
              {_TPL_CATEGORIES(t).filter(c => c.id).map(c => (
                <option key={c.id} value={c.id}>{c.label} · {c.id}</option>
              ))}
            </select>
          </_TplField>
          <_TplField label={t.tx('目标页', 'Target page')}>
            <select className="select" value={form.target_page} onChange={e => set('target_page', e.target.value)}>
              <option value="home">home</option>
              <option value="promos">promos</option>
              <option value="me">me</option>
            </select>
          </_TplField>
          <_TplField label={t.tx('预览图 URL', 'Preview image URL')}>
            <input className="input" value={form.preview_image_url}
                   onChange={e => set('preview_image_url', e.target.value)}
                   placeholder="https://…" />
          </_TplField>
        </div>

        <_TplField label={t.tx('标签', 'Tags')} hint={t.tx('逗号分隔，例：春节, 节日, 红包', 'Comma-separated, e.g. spring, festival, red packet')}>
          <input className="input" value={form.tags} onChange={e => set('tags', e.target.value)}
                 placeholder={t.tx('春节, 节日, 红包', 'spring, festival, red packet')} />
        </_TplField>
      </div>
    </Modal>
  );
}

// ─── 时间格式化（版本历史用） ───────────────────────────────────────────
function _tplFmtAbs(iso) {
  if (!iso) return '—';
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return String(iso);
    const pad = n => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  } catch (_e) { return String(iso); }
}
function _tplFmtRel(iso, t) {
  if (!iso) return '—';
  const d = new Date(iso);
  if (isNaN(d.getTime())) return String(iso);
  const sec = Math.floor((Date.now() - d.getTime()) / 1000);
  if (sec < 0)   return t.tx('刚刚', 'just now');
  if (sec < 60)  return t.tx(`${sec}秒前`, `${sec}s ago`);
  const m = Math.floor(sec / 60);
  if (m < 60)    return t.tx(`${m}分钟前`, `${m}m ago`);
  const h = Math.floor(m / 60);
  if (h < 24)    return t.tx(`${h}小时前`, `${h}h ago`);
  const dy = Math.floor(h / 24);
  if (dy < 30)   return t.tx(`${dy}天前`, `${dy}d ago`);
  const mo = Math.floor(dy / 30);
  if (mo < 12)   return t.tx(`${mo}个月前`, `${mo}mo ago`);
  return t.tx(`${Math.floor(mo / 12)}年前`, `${Math.floor(mo / 12)}y ago`);
}

// ─── 版本历史 tab ──────────────────────────────────────────────────────
function _TplVersionsTab({ tpl, t, push, basicsReloadKey, onAfterRollback }) {
  const [rows, setRows]       = React.useState(null);
  const [err, setErr]         = React.useState(null);
  const [tick, setTick]       = React.useState(0);
  const [viewing, setViewing] = React.useState(null);   // { rev, loading, data, err }
  const [rollingRev, setRollingRev] = React.useState(null);

  React.useEffect(() => {
    let alive = true;
    setRows(null); setErr(null);
    (async () => {
      try {
        const r = await fetch(`/api/admin/templates/${tpl.id}/versions`, { headers: window.NebulaAdmin.authHeaders() });
        if (!r.ok) throw new Error('HTTP ' + r.status);
        const j = await r.json();
        if (!alive) return;
        setRows(Array.isArray(j) ? j : []);
      } catch (e) {
        if (alive) setErr(e);
      }
    })();
    return () => { alive = false; };
  }, [tpl.id, tick, basicsReloadKey]);

  const headRev = React.useMemo(() => {
    if (!rows || !rows.length) return null;
    return rows.reduce((m, r) => (r.rev > m ? r.rev : m), rows[0].rev);
  }, [rows]);

  const doView = async (rev) => {
    setViewing({ rev, loading: true, data: null, err: null });
    try {
      const r = await fetch(`/api/admin/templates/${tpl.id}/versions/${rev}`, { headers: window.NebulaAdmin.authHeaders() });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const j = await r.json();
      setViewing({ rev, loading: false, data: j, err: null });
    } catch (e) {
      setViewing({ rev, loading: false, data: null, err: e });
    }
  };

  const doRollback = async (rev) => {
    if (!confirm(t.tx(`确认回滚到 rev ${rev}？这会创建一个新版本，把模板组件覆盖为该版本内容。`, `Confirm rollback to rev ${rev}? This creates a new revision that overwrites the template components with that version.`))) return;
    const note = prompt(t.tx(`备注（可选）：为何回滚到 rev ${rev}？`, `Note (optional): why roll back to rev ${rev}?`), '');
    if (note === null) return; // cancel
    setRollingRev(rev);
    try {
      const r = await fetch(`/api/admin/templates/${tpl.id}/rollback`, {
        method: 'POST',
        headers: window.NebulaAdmin.authHeaders({ 'Content-Type': 'application/json' }),
        body: JSON.stringify({ rev, change_note: note || undefined }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok || !j.ok) throw new Error(j.error || ('HTTP ' + r.status));
      push(t.tx(`已回滚到 rev ${rev} · 新版本 rev ${j.new_rev}`, `Rolled back to rev ${rev} · new rev ${j.new_rev}`));
      setViewing(null);
      setTick(v => v + 1);
      if (typeof onAfterRollback === 'function') onAfterRollback();
    } catch (e) {
      push(t.tx('回滚失败: ', 'Rollback failed: ') + (e.body?.error || e.message || e));
    } finally {
      setRollingRev(null);
    }
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <div className="text-faint" style={{ fontSize: 11, flex: 1 }}>
          {t.tx('每次保存或回滚都会产生新 rev。当前 head 不可回滚到自己。', 'Each save or rollback creates a new rev. The current head cannot roll back to itself.')}
        </div>
        <button className="btn btn-sm" onClick={() => setTick(v => v + 1)}>{t.tx('刷新', 'Refresh')}</button>
      </div>

      {err && (
        <div style={{ padding: 10, color: 'var(--danger)', fontSize: 12, background: 'var(--bg-inset)', borderRadius: 6 }}>
          {t.tx('加载失败: ', 'Load failed: ')}{String(err.message || err)}
        </div>
      )}
      {!rows && !err && (
        <div className="text-faint" style={{ fontSize: 12, padding: 12, textAlign: 'center' }}>{t.tx('加载中…', 'Loading…')}</div>
      )}
      {rows && rows.length === 0 && (
        <div className="text-faint" style={{ fontSize: 12, padding: 12, textAlign: 'center' }}>{t.tx('暂无历史版本', 'No prior revisions')}</div>
      )}
      {rows && rows.length > 0 && (
        <div className="tbl-scroll" style={{ maxHeight: 320, overflow: 'auto', border: '1px solid var(--border)', borderRadius: 6 }}>
          <table className="tbl-stack" style={{ width: '100%', fontSize: 12, borderCollapse: 'collapse' }}>
            <thead style={{ position: 'sticky', top: 0, background: 'var(--bg-card)' }}>
              <tr style={{ color: 'var(--text-muted)', textAlign: 'left' }}>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)', width: 56 }}>rev</th>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)' }}>{t.tx('编辑人', 'Author')}</th>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)' }}>{t.tx('时间', 'At')}</th>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)' }}>{t.tx('备注', 'Note')}</th>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)', width: 64, textAlign: 'right' }}>{t.tx('组件数', 'Comps')}</th>
                <th style={{ padding: '6px 8px', borderBottom: '1px solid var(--border)', width: 150, textAlign: 'right' }}>{t.tx('操作', 'Actions')}</th>
              </tr>
            </thead>
            <tbody>
              {[...rows].sort((a, b) => b.rev - a.rev).map(row => {
                const isHead = row.rev === headRev;
                return (
                  <tr key={row.rev} style={{ borderBottom: '1px solid var(--border)' }}>
                    <td data-label="rev" className="tabular mono" style={{ padding: '6px 8px', fontWeight: 600 }}>
                      {row.rev}
                      {isHead && <span className="chip success" style={{ marginLeft: 6, fontSize: 9 }}>{t.tx('当前', 'HEAD')}</span>}
                    </td>
                    <td data-label={t.tx('编辑人', 'Author')} style={{ padding: '6px 8px' }}>{row.edited_by || <span className="text-faint">—</span>}</td>
                    <td data-label={t.tx('时间', 'At')} className="tabular" style={{ padding: '6px 8px' }} title={_tplFmtAbs(row.edited_at)}>
                      {_tplFmtRel(row.edited_at, t)}
                    </td>
                    <td data-label={t.tx('备注', 'Note')} style={{ padding: '6px 8px', color: 'var(--text-muted)' }}>
                      {row.change_note || <span className="text-faint">—</span>}
                    </td>
                    <td data-label={t.tx('组件数', 'Comps')} className="tabular" style={{ padding: '6px 8px', textAlign: 'right' }}>{row.comp_count}</td>
                    <td data-label={t.tx('操作', 'Actions')} style={{ padding: '6px 8px', textAlign: 'right', whiteSpace: 'nowrap' }}>
                      <button className="btn btn-sm" onClick={() => doView(row.rev)}>{t.tx('查看', 'View')}</button>
                      <button className="btn btn-sm btn-danger"
                              style={{ marginLeft: 4 }}
                              disabled={isHead || rollingRev === row.rev}
                              title={isHead ? t.tx('当前版本无法回滚到自身', 'Current head cannot roll back to itself') : ''}
                              onClick={() => doRollback(row.rev)}>
                        {rollingRev === row.rev ? t.tx('回滚中…', 'Rolling…') : t.tx('回滚', 'Rollback')}
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      {viewing && (
        <div style={{ border: '1px solid var(--border)', borderRadius: 6, background: 'var(--bg-inset)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', borderBottom: '1px solid var(--border)' }}>
            <b style={{ fontSize: 12 }}>{t.tx('查看版本 rev ', 'Viewing rev ')}{viewing.rev}</b>
            {viewing.data && (
              <span className="text-faint" style={{ fontSize: 11 }}>
                · {viewing.data.edited_by || '—'} · {_tplFmtAbs(viewing.data.edited_at)}
                {viewing.data.change_note ? ` · ${viewing.data.change_note}` : ''}
              </span>
            )}
            <div style={{ flex: 1 }} />
            <button className="btn btn-sm" onClick={() => setViewing(null)}>{t.tx('关闭', 'Close')}</button>
          </div>
          <div style={{ padding: 10 }}>
            {viewing.loading && <div className="text-faint" style={{ fontSize: 11 }}>{t.tx('加载中…', 'Loading…')}</div>}
            {viewing.err && <div style={{ color: 'var(--danger)', fontSize: 11 }}>{String(viewing.err.message || viewing.err)}</div>}
            {viewing.data && (
              <pre className="mono"
                   style={{ margin: 0, fontSize: 11, maxHeight: 260, overflow: 'auto', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
                {JSON.stringify(viewing.data.components || [], null, 2)}
              </pre>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── 编辑 modal：基本信息 / 组件 / 版本历史 ─────────────────────────────
function TemplateEditModal({ tpl, onClose, onSaved, push, t }) {
  const [tab, setTab]         = React.useState('basics');
  const [reloadKey, setReloadKey] = React.useState(0);
  const [tplData, setTplData] = React.useState(tpl);

  const [form, setForm] = React.useState({
    name:        tplData.name || '',
    description: tplData.description || '',
    category:    tplData.category || 'general',
    target_page: tplData.target_page || 'home',
    preview_image_url: tplData.preview_image_url || '',
    tags:        (tplData.tags || []).join(', '),
  });
  const [busy, setBusy] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  // 回滚后刷新 basics 表单 & 模板对象
  const reloadBasics = React.useCallback(async () => {
    try {
      const r = await _tplFetch(`/api/templates/${tpl.id}`);
      const fresh = (r && r.template) ? r.template : r;
      if (fresh && fresh.id) {
        setTplData(fresh);
        setForm({
          name:        fresh.name || '',
          description: fresh.description || '',
          category:    fresh.category || 'general',
          target_page: fresh.target_page || 'home',
          preview_image_url: fresh.preview_image_url || '',
          tags:        (fresh.tags || []).join(', '),
        });
      }
      setReloadKey(k => k + 1);
    } catch (_e) {
      // 静默：版本列表会通过 reloadKey 自动重拉
      setReloadKey(k => k + 1);
    }
  }, [tpl.id]);

  const submit = async () => {
    if (!form.name.trim()) { push(t.tx('名称必填', 'Name is required')); return; }
    setBusy(true);
    try {
      const payload = {
        name:        form.name.trim(),
        description: form.description,
        category:    form.category,
        target_page: form.target_page,
        preview_image_url: form.preview_image_url.trim() || undefined,
        tags:        form.tags.split(/[,，]\s*/).map(s => s.trim()).filter(Boolean),
      };
      await _tplFetch(`/api/admin/templates/${tpl.id}`, {
        method: 'PUT',
        body: JSON.stringify(payload),
      });
      push(t.tx('已保存', 'Saved'));
      onSaved();
    } catch (e) {
      push(t.tx('保存失败: ', 'Save failed: ') + (e.body?.error || e.message || e));
    } finally {
      setBusy(false);
    }
  };

  const tabBtn = (key, zh, en) => (
    <button key={key}
            type="button"
            className={'chip ' + (tab === key ? 'active' : '')}
            style={{ fontSize: 11 }}
            onClick={() => setTab(key)}>
      {t.tx(zh, en)}
    </button>
  );

  const footer = tab === 'basics'
    ? <>
        <button className="btn btn-sm" onClick={onClose}>{t.tx('取消', 'Cancel')}</button>
        <button className="btn btn-pri btn-sm" disabled={busy} onClick={submit}>
          {busy ? t.tx('提交中…', 'Submitting…') : t.tx('保存', 'Save')}
        </button>
      </>
    : <button className="btn btn-sm" onClick={onClose}>{t.tx('关闭', 'Close')}</button>;

  return (
    <Modal open={true} onClose={onClose}
           title={t.tx('编辑模板 · ', 'Edit Template · ') + tpl.id}
           width={tab === 'versions' ? 720 : 520}
           footer={footer}>
      <div style={{ display: 'flex', gap: 6, marginBottom: 12, paddingBottom: 8, borderBottom: '1px solid var(--border)' }}>
        {tabBtn('basics',     '基本信息', 'Basics')}
        {tabBtn('components', '组件',     'Components')}
        {tabBtn('versions',   '版本历史', 'Versions')}
      </div>

      {tab === 'basics' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div style={{ fontSize: 11, color: 'var(--text-faint)', padding: '6px 10px', background: 'var(--bg-inset)', borderRadius: 6 }}>
            {t.tx('仅修改元数据 · components 数组通过装修编辑器维护', 'Edit metadata only · components array is managed by the layout editor')}
          </div>
          <_TplField label={t.tx('名称 *', 'Name *')}>
            <input className="input" value={form.name} onChange={e => set('name', e.target.value)} />
          </_TplField>
          <_TplField label={t.tx('描述', 'Description')}>
            <textarea className="textarea" rows={2}
                      value={form.description} onChange={e => set('description', e.target.value)} />
          </_TplField>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <_TplField label={t.tx('分类', 'Category')}>
              <select className="select" value={form.category} onChange={e => set('category', e.target.value)}>
                {_TPL_CATEGORIES(t).filter(c => c.id).map(c => (
                  <option key={c.id} value={c.id}>{c.label} · {c.id}</option>
                ))}
              </select>
            </_TplField>
            <_TplField label={t.tx('目标页', 'Target page')}>
              <select className="select" value={form.target_page} onChange={e => set('target_page', e.target.value)}>
                <option value="home">home</option>
                <option value="promos">promos</option>
                <option value="me">me</option>
              </select>
            </_TplField>
          </div>
          <_TplField label={t.tx('预览图 URL', 'Preview image URL')}>
            <input className="input" value={form.preview_image_url}
                   onChange={e => set('preview_image_url', e.target.value)} />
          </_TplField>
          <_TplField label={t.tx('标签', 'Tags')} hint={t.tx('逗号分隔', 'Comma-separated')}>
            <input className="input" value={form.tags} onChange={e => set('tags', e.target.value)} />
          </_TplField>
        </div>
      )}

      {tab === 'components' && (
        <div className="text-faint" style={{ fontSize: 12, padding: 24, textAlign: 'center', lineHeight: 1.6 }}>
          {t.tx('组件数组通过装修编辑器（页面装修页）维护。', 'Components array is managed by the layout editor (Page Decoration screen).')}
          <br />
          {t.tx('如需查看历史 components JSON，请切到「版本历史」并点击“查看”。', 'To inspect components JSON, switch to "Versions" tab and click "View".')}
        </div>
      )}

      {tab === 'versions' && (
        <_TplVersionsTab tpl={tplData}
                        t={t}
                        push={push}
                        basicsReloadKey={reloadKey}
                        onAfterRollback={reloadBasics} />
      )}
    </Modal>
  );
}

Object.assign(window, { TemplatesScreen, TemplateCard, TemplateCreateModal, TemplateEditModal });
