// template-usage.jsx — Template usage page (/template/:id)
// Left: template list | Middle: upload + create | Right: task results

const _tplToken = () => window.YiteAPI?.getToken?.() || localStorage.getItem('token') || '';
const _tplAPI = {
  list: (page = 1) =>
    fetch('/api/v1/templates?category=template&page_size=50&page=' + page).then(r => r.json()),
  get: id =>
    fetch('/api/v1/templates/' + id).then(r => r.json()),
  upload: file => {
    const fd = new FormData(); fd.append('file', file); fd.append('parent_id', '');
    return fetch('/api/v1/assets/upload', { method: 'POST', headers: { Authorization: 'Bearer ' + _tplToken() }, body: fd }).then(r => r.json());
  },
  createTask: body =>
    fetch('/api/v1/create-tasks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + _tplToken() },
      body: JSON.stringify(body),
    }).then(r => r.json()),
  listTasks: (templateId) =>
    fetch('/api/v1/create-tasks?media_type=template' + (templateId ? '&template_id=' + encodeURIComponent(templateId) : ''), { headers: { Authorization: 'Bearer ' + _tplToken() } }).then(r => r.json()),
  publishWork: body =>
    fetch('/api/v1/templates', {
      method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + _tplToken() },
      body: JSON.stringify(body),
    }).then(r => r.json()),
  unpublish: templateId =>
    fetch('/api/v1/templates/' + templateId + '/unpublish', {
      method: 'POST', headers: { Authorization: 'Bearer ' + _tplToken() },
    }).then(r => r.json()),
  uploadCover: file => {
    const fd = new FormData(); fd.append('file', file);
    return fetch('/api/v1/templates/cover', { method: 'POST', headers: { Authorization: 'Bearer ' + _tplToken() }, body: fd }).then(r => r.json());
  },
  deleteTask: id =>
    fetch('/api/v1/create-tasks/' + id, { method: 'DELETE', headers: { Authorization: 'Bearer ' + _tplToken() } }).then(r => r.json()),
};

const _tplAssetList = (parentID = '', tag = '') => {
  let url = '/api/v1/assets?parent_id=' + encodeURIComponent(parentID);
  if (tag) url += '&tag=' + encodeURIComponent(tag);
  return fetch(url, { headers: { Authorization: 'Bearer ' + _tplToken() } }).then(r => r.json());
};

// ─── Asset picker modal (select from library) ───────────────────────────────
const TplAssetPicker = ({ max = 6, existing = 0, onSelect, onClose }) => {
  const { t } = useI18n();
  const [folders, setFolders] = React.useState([]);
  const [files, setFiles] = React.useState([]);
  const [path, setPath] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [selected, setSelected] = React.useState(new Set());
  const uploadRef = React.useRef(null);
  const parentID = path.length > 0 ? path[path.length - 1].id : '';
  const remaining = max - existing;

  const fetchItems = async () => {
    setLoading(true);
    const res = await _tplAssetList(parentID, '');
    const items = res.items || [];
    setFolders(items.filter(i => i.is_folder));
    setFiles(items.filter(i => !i.is_folder && i.tag === 'image'));
    setLoading(false);
  };
  React.useEffect(() => { fetchItems(); }, [parentID]);

  const toggleSelect = id => {
    setSelected(prev => { const next = new Set(prev); next.has(id) ? next.delete(id) : (next.size < remaining && next.add(id)); return next; });
  };
  const handleUpload = async e => {
    const f = e.target.files?.[0]; e.target.value = '';
    if (!f) return;
    setLoading(true);
    await _tplAPI.upload(f);
    fetchItems();
  };
  const handleConfirm = () => {
    const picked = files.filter(f => selected.has(f.id)).map(f => ({ id: f.id, url: f.url, name: f.name }));
    onSelect(picked);
    onClose();
  };

  return ReactDOM.createPortal(
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.65)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 200 }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{ width: 620, maxHeight: '80vh', background: '#141722', border: '1px solid var(--line-2)', borderRadius: 14, display: 'flex', flexDirection: 'column', boxShadow: '0 24px 60px rgba(0,0,0,0.6)' }} onClick={e => e.stopPropagation()}>
        <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ fontSize: 17, fontWeight: 700, color: 'var(--fg-0)' }}>{t('tpl.selectImages')}</span>
            <span style={{ fontSize: 13, color: 'var(--fg-3)' }}>{t('tpl.canSelect')} {remaining - selected.size} {t('tpl.items')}</span>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 'none', color: 'var(--fg-3)', cursor: 'pointer', fontSize: 18 }}>✕</button>
        </div>
        <div style={{ padding: '10px 22px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 5, fontSize: 14, flexShrink: 0 }}>
          <button onClick={() => setPath([])} style={{ background: 'none', border: 'none', color: path.length === 0 ? 'var(--fg-0)' : 'var(--accent)', cursor: 'pointer', fontSize: 14, fontWeight: path.length === 0 ? 600 : 400 }}>{t('assets.myLibrary')}</button>
          {path.map((p, i) => (
            <React.Fragment key={i}>
              <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="var(--fg-3)" strokeWidth="2.5" strokeLinecap="round"><polyline points="9 18 15 12 9 6"/></svg>
              <button onClick={() => setPath(path.slice(0, i + 1))} style={{ background: 'none', border: 'none', color: i === path.length - 1 ? 'var(--fg-0)' : 'var(--accent)', cursor: 'pointer', fontSize: 14, fontWeight: i === path.length - 1 ? 600 : 400 }}>{p.name}</button>
            </React.Fragment>
          ))}
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: 16 }}>
          <input ref={uploadRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={handleUpload} />
          {loading ? (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 40 }}><div className="yt-spinner" /></div>
          ) : (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: 10 }}>
              <button onClick={() => uploadRef.current?.click()} style={{ aspectRatio: '1', borderRadius: 8, border: '1.5px dashed var(--line-2)', background: 'rgba(255,255,255,0.02)', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 6, color: 'var(--fg-3)' }}>
                <Icon name="upload" size={20} /><span style={{ fontSize: 12 }}>{t('common.upload')}</span>
              </button>
              {folders.map(f => {
                const hue = (f.name.charCodeAt(0) * 37) % 360;
                return (
                  <div key={f.id} onClick={() => setPath([...path, { id: f.id, name: f.name }])} style={{ aspectRatio: '1', borderRadius: 8, border: '1px solid var(--line-2)', background: `hsl(${hue} 50% 12%)`, cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
                    <svg width="28" height="28" viewBox="0 0 24 24" fill={`hsl(${hue} 70% 55%)`}><path d="M3 7a2 2 0 012-2h3.586a1 1 0 01.707.293L10.707 6.7A1 1 0 0011.414 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7z"/></svg>
                    <span style={{ fontSize: 12, color: 'var(--fg-1)', fontWeight: 500, maxWidth: '90%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</span>
                  </div>
                );
              })}
              {files.map(f => {
                const isSel = selected.has(f.id);
                return (
                  <div key={f.id} onClick={() => toggleSelect(f.id)} style={{ aspectRatio: '1', borderRadius: 8, overflow: 'hidden', position: 'relative', border: `2px solid ${isSel ? 'var(--accent)' : 'var(--line-2)'}`, cursor: 'pointer' }}>
                    {f.url ? <img src={f.url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> : <div style={{ width: '100%', height: '100%', background: 'rgba(255,255,255,0.04)' }} />}
                    {isSel && <div style={{ position: 'absolute', top: 4, right: 4, width: 20, height: 20, borderRadius: '50%', background: 'var(--accent)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="#052028" strokeWidth="2.5" strokeLinecap="round"><polyline points="2,6 5,9 10,3"/></svg></div>}
                  </div>
                );
              })}
            </div>
          )}
        </div>
        <div style={{ padding: '14px 22px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'flex-end', gap: 8, flexShrink: 0 }}>
          <button onClick={onClose} style={{ height: 38, padding: '0 18px', borderRadius: 8, border: '1px solid var(--line-2)', background: 'transparent', color: 'var(--fg-1)', fontSize: 14, cursor: 'pointer' }}>{t('common.cancel')}</button>
          <button onClick={handleConfirm} disabled={selected.size === 0} style={{ height: 38, padding: '0 22px', borderRadius: 8, border: 'none', background: selected.size > 0 ? 'var(--accent)' : 'rgba(255,255,255,0.06)', color: selected.size > 0 ? '#052028' : 'var(--fg-3)', fontSize: 14, fontWeight: 600, cursor: selected.size > 0 ? 'pointer' : 'not-allowed' }}>
            {t('tpl.confirmSelect')} {selected.size > 0 ? `(${selected.size})` : ''}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
};

const _isVidUrl = url => /\.(mp4|mov|webm|avi|mkv)(\?|$)/i.test(url);
const _relTime = iso => {
  if (!iso) return '';
  if (window.relativeTime) return window.relativeTime(iso);
  const d = (Date.now() - new Date(iso).getTime()) / 1000;
  const t = window.t || (k => k);
  if (d < 60) return t('time.justNow'); if (d < 3600) return Math.floor(d / 60) + t('time.minutesAgo');
  if (d < 86400) return Math.floor(d / 3600) + t('time.hoursAgo'); if (d < 604800) return Math.floor(d / 86400) + t('time.daysAgo');
  return new Date(iso).toLocaleDateString();
};

// ─── Sidebar template card ───────────────────────────────────────────────────
const TplSidebarCard = ({ tpl, active, onClick }) => {
  const { t } = useI18n();
  const src = tpl.cover_url || tpl.result_url;
  const isVid = tpl.media_type === 'video';
  return (
    <div onClick={onClick} style={{
      display: 'flex', gap: 12, padding: '12px 14px', borderRadius: 10, cursor: 'pointer',
      background: active ? 'rgba(34,211,238,0.08)' : 'transparent',
      border: active ? '1px solid rgba(34,211,238,0.3)' : '1px solid transparent',
      transition: 'all .12s',
    }}
      onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; }}
      onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}>
      <div style={{ width: 56, height: 56, borderRadius: 8, overflow: 'hidden', flexShrink: 0, background: '#0a0e1a' }}>
        {src && (isVid && _isVidUrl(src)
          ? <video src={src} muted playsInline preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
          : <img src={src} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} onError={e => e.target.style.display = 'none'} />
        )}
      </div>
      <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 4 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: active ? 'var(--fg-0)' : 'var(--fg-1)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {tpl.title || t('tpl.untitled')}
        </div>
        <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>
          {isVid ? t('shared.badge.video') : t('creation.image')}{tpl.use_count > 0 ? ' · ' + tpl.use_count + ' ' + t('tpl.usageCount') : ''}
        </div>
      </div>
    </div>
  );
};

// ─── Task card (simplified for template page) ────────────────────────────────
const TplTaskCard = ({ task, isCurrent, onDelete, onPublish, onUnpublish }) => {
  const { t } = useI18n();
  const [publishing, setPub] = React.useState(false);
  const [preview, setPreview] = React.useState(null);
  const isRunning = task.status === 'pending' || task.status === 'running';
  const isDone = task.status === 'done';
  const statusColor = task.status === 'error' ? '#f87171' : isRunning ? 'var(--accent)' : '#4ade80';
  const statusLabel = task.status === 'pending' ? t('status.queued') : task.status === 'running' ? t('status.generating') : isDone ? t('status.completed') : t('status.failed');
  const resultUrl = task.results?.[0];
  const mp = task.model_param || {};

  return (
    <div style={{ borderRadius: 12, background: 'rgba(255,255,255,0.02)', border: isCurrent ? '1px solid rgba(34,211,238,0.3)' : '1px solid var(--line)', overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 8 }}>
        {isCurrent && <span style={{ fontSize: 10, padding: '2px 6px', borderRadius: 4, background: 'rgba(34,211,238,0.15)', color: 'var(--accent)', fontWeight: 600 }}>{t('tpl.currentTpl')}</span>}
        <span style={{ fontSize: 11, padding: '2px 8px', borderRadius: 4, background: isRunning ? 'rgba(34,211,238,0.15)' : task.status === 'error' ? 'rgba(248,113,113,0.15)' : 'rgba(74,222,128,0.12)', color: statusColor, fontWeight: 600 }}>{statusLabel}</span>
        <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{_relTime(task.created_at)}</span>
        <span style={{ fontSize: 11, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)' }}>{[mp.aspect_ratio, mp.resolution, mp.duration ? mp.duration + 's' : null].filter(Boolean).join(' · ')}</span>
      </div>
      {task.prompt && <div style={{ padding: '0 16px 10px', fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.5, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{task.prompt}</div>}
      {resultUrl && (
        <div onClick={() => setPreview(resultUrl)} style={{ margin: '0 16px 12px', borderRadius: 10, overflow: 'hidden', cursor: 'pointer', background: '#06080f' }}>
          {_isVidUrl(resultUrl) ? (
            <video src={resultUrl} muted playsInline preload="metadata" onMouseEnter={e => e.target.play().catch(() => {})} onMouseLeave={e => { e.target.pause(); e.target.currentTime = 0; }}
              style={{ width: '100%', display: 'block', maxHeight: 300, objectFit: 'contain' }} />
          ) : (
            <img src={resultUrl} alt="" style={{ width: '100%', display: 'block', maxHeight: 300, objectFit: 'contain' }} />
          )}
        </div>
      )}
      {isRunning && !resultUrl && (
        <div style={{ margin: '0 16px 12px', borderRadius: 10, aspectRatio: '16/9', background: 'rgba(255,255,255,0.02)', border: '1px solid var(--line)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 10 }}>
          <div className="yt-spinner" style={{ width: 28, height: 28 }} />
          <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{statusLabel}…</span>
        </div>
      )}
      {task.error_msg && <div style={{ margin: '0 16px 12px', padding: '8px 12px', borderRadius: 8, background: 'rgba(248,113,113,0.08)', border: '1px solid rgba(248,113,113,0.2)', fontSize: 12, color: '#fca5a5' }}>{task.error_msg}</div>}
      <div style={{ padding: '8px 16px 12px', display: 'flex', alignItems: 'center', gap: 8 }}>
        {isDone && (
          <button disabled={publishing} onClick={async () => {
            setPub(true);
            if (task.published) { await onUnpublish(task); } else { await onPublish(task); }
            setPub(false);
          }} style={{
            height: 30, padding: '0 14px', borderRadius: 8,
            border: task.published ? '1px solid rgba(74,222,128,0.3)' : '1px solid var(--line-2)',
            background: task.published ? 'rgba(74,222,128,0.08)' : 'rgba(255,255,255,0.04)',
            color: task.published ? '#4ade80' : 'var(--fg-1)',
            fontSize: 12, fontWeight: 500, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 5,
            opacity: publishing ? 0.6 : 1,
          }}>
            {task.published ? '✓ ' + t('status.submitted') : t('tpl.submitWork')}
          </button>
        )}
        <div style={{ flex: 1 }} />
        <button onClick={async () => { if (await ytConfirm(t('confirm.deleteTask'), { danger: true })) onDelete(task.id); }} style={{ height: 30, padding: '0 10px', borderRadius: 8, border: 'none', background: 'transparent', color: 'var(--fg-3)', fontSize: 12, cursor: 'pointer' }}
          onMouseEnter={e => e.currentTarget.style.color = '#f87171'} onMouseLeave={e => e.currentTarget.style.color = 'var(--fg-3)'}>
          {t('common.delete')}
        </button>
      </div>
      {preview && ReactDOM.createPortal(
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.85)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 300, cursor: 'pointer' }} onClick={() => setPreview(null)}>
          <button onClick={() => setPreview(null)} style={{ position: 'absolute', top: 16, right: 16, background: 'rgba(255,255,255,0.1)', border: 'none', color: '#fff', width: 36, height: 36, borderRadius: '50%', fontSize: 18, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>✕</button>
          {_isVidUrl(preview) ? <video src={preview} controls autoPlay style={{ maxWidth: '90vw', maxHeight: '90vh', borderRadius: 8 }} onClick={e => e.stopPropagation()} />
            : <img src={preview} alt="" style={{ maxWidth: '90vw', maxHeight: '90vh', borderRadius: 8, objectFit: 'contain' }} onClick={e => e.stopPropagation()} />}
        </div>, document.body
      )}
    </div>
  );
};

// ─── Main screen ──────────────────────────────────────────────────────────────
const TemplateUsageScreen = ({ templateId }) => {
  const { t } = useI18n();
  const { isMobile, isTablet, w } = useBreakpoint();
  const isCompact = isMobile || isTablet;
  const tplSidebarW = w >= 3840 ? '360px' : w >= 2560 ? '300px' : '240px';
  const [templates, setTemplates] = React.useState([]);
  const [currentId, setCurrentId] = React.useState(templateId);
  const [tplData, setTplData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [imageSlots, setImageSlots] = React.useState([]); // [{role, original_url, user_url, user_name}]
  const [creating, setCreating] = React.useState(false);
  const [error, setError] = React.useState('');
  const [tasks, setTasks] = React.useState([]);
  const [taskFilter, setTaskFilter] = React.useState('current'); // 'all' | 'current'
  const [pickerSlotIdx, setPickerSlotIdx] = React.useState(null); // which slot is picking

  React.useEffect(() => {
    _tplAPI.list().then(d => {
      const items = d.items || [];
      setTemplates(items);
      // Auto-select: pick a random one from top popular templates
      if ((!currentId || currentId === '_') && items.length > 0) {
        const sorted = [...items].sort((a, b) => (b.use_count || 0) - (a.use_count || 0));
        const topN = sorted.slice(0, Math.min(5, sorted.length));
        const pick = topN[Math.floor(Math.random() * topN.length)];
        setCurrentId(pick.id);
        window.history.replaceState({}, '', '/template/' + pick.id);
      }
    });
  }, []);

  React.useEffect(() => {
    if (!currentId) return;
    setLoading(true); setError(''); setImageSlots([]);
    _tplAPI.get(currentId).then(data => {
      setTplData(data);
      // Initialize image slots — prefer template's own image_list, fallback to task's
      const origImages = data.image_list || data.task?.image_list || [];
      if (origImages.length > 0) {
        setImageSlots(origImages.map((img, i) => ({
          role: img.role || 'reference_image',
          original_url: img.image_url,
          user_url: null,
          user_name: null,
          label: img.role === 'first_frame' ? t('video.startFrame') : img.role === 'last_frame' ? t('video.endFrame') : t('creation.image') + ' ' + (i + 1),
        })));
      } else if ((data.image_count || 0) > 0) {
        setImageSlots(Array.from({ length: data.image_count }, (_, i) => ({
          role: 'reference_image',
          original_url: null,
          user_url: null,
          user_name: null,
          label: t('creation.image') + ' ' + (i + 1),
        })));
      }
      setLoading(false);
    }).catch(() => { setError(t('tpl.loadFailed')); setLoading(false); });
  }, [currentId]);

  // Fetch tasks — all template tasks or current template only
  const fetchTasks = () => {
    const tplId = taskFilter === 'current' && currentId && currentId !== '_' ? currentId : '';
    _tplAPI.listTasks(tplId).then(d => { if (d.items) setTasks(d.items); });
  };
  React.useEffect(() => { fetchTasks(); }, [currentId, taskFilter]);
  React.useEffect(() => {
    if (!tasks.some(t => t.status === 'pending' || t.status === 'running')) return;
    const id = setInterval(fetchTasks, 5000);
    return () => clearInterval(id);
  }, [tasks]);

  const handleSwitchTemplate = id => { setCurrentId(id); window.history.replaceState({}, '', '/template/' + id); };

  const handleSlotPicked = picked => {
    if (pickerSlotIdx == null || picked.length === 0) return;
    setImageSlots(prev => prev.map((s, i) => i === pickerSlotIdx ? { ...s, user_url: picked[0].url, user_name: picked[0].name } : s));
    setPickerSlotIdx(null);
  };

  const handleCreate = async () => {
    if (!tplData) { setError(t('tpl.templateNotLoaded')); return; }
    if (!_tplToken()) { showLoginModal(); return; }
    // Check all image slots are filled
    const unfilled = imageSlots.filter(s => !s.user_url);
    if (unfilled.length > 0) { setError(t('tpl.missingImages') + unfilled.length + ' ' + t('tpl.items') + ')'); return; }
    setCreating(true); setError('');
    // Use template's own data (self-contained), fallback to task for legacy
    const origTask = tplData.task;
    const newImageList = imageSlots.map(s => ({ image_url: s.user_url, role: s.role }));
    try {
      const res = await _tplAPI.createTask({
        media_type: 'template',
        type: tplData.task_type || origTask?.type || (tplData.media_type === 'video' ? '参考生视频' : '参考生图'),
        prompt: tplData.prompt || origTask?.prompt,
        model: tplData.model_id || origTask?.model,
        model_param: tplData.model_param || origTask?.model_param || {},
        image_list: newImageList,
        template_id: tplData.id,
        folder_id: '',
      });
      if (res.error) { setError(res.error); setCreating(false); return; }
      setCreating(false);
      // Reset slots to empty for next creation
      setImageSlots(prev => prev.map(s => ({ ...s, user_url: null, user_name: null })));
      fetchTasks();
    } catch (e) { setError(e.message); setCreating(false); }
  };

  const handlePublish = async task => {
    const title = await ytPrompt(t('tpl.inputWorkTitle'));
    if (!title) return;
    let coverOssKey = '';
    if (task.media_type === 'video' && task.results?.[0]) {
      try {
        const blob = await new Promise((resolve, reject) => {
          const v = document.createElement('video'); v.crossOrigin = 'anonymous'; v.src = task.results[0];
          v.onloadeddata = () => { v.currentTime = 0.1; };
          v.onseeked = () => { const c = document.createElement('canvas'); c.width = v.videoWidth; c.height = v.videoHeight; c.getContext('2d').drawImage(v, 0, 0); c.toBlob(b => b ? resolve(b) : reject(), 'image/png'); };
          v.onerror = () => reject();
        });
        const r = await _tplAPI.uploadCover(new File([blob], 'cover.png', { type: 'image/png' }));
        if (r.oss_key) coverOssKey = r.oss_key;
      } catch (e) {}
    }
    await _tplAPI.publishWork({ task_id: task.id, title, is_public: true, is_template: false, cover_oss_key: coverOssKey });
    fetchTasks();
  };

  const handleUnpublish = async task => {
    if (!task.template_id) return;
    await _tplAPI.unpublish(task.template_id);
    fetchTasks();
  };

  const handleDeleteTask = async id => { await _tplAPI.deleteTask(id); fetchTasks(); };

  const tpl = tplData;
  const task = tpl?.task;
  const mp = task?.model_param || tpl?.model_param || {};
  const needImages = tpl?.image_count || 0;
  const previewSrc = tpl?.cover_url || tpl?.result_url;
  const isVideo = tpl?.media_type === 'video';
  const metaChips = [mp.aspect_ratio, mp.resolution, mp.duration ? mp.duration + 's' : null].filter(Boolean);

  return (
    <AppShell active="template">
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>

        {/* ── Mobile/Tablet: horizontal template strip ── */}
        {isCompact && templates.length > 0 && (
          <div style={{ display: 'flex', gap: 8, overflowX: 'auto', padding: '12px 16px', borderBottom: '1px solid var(--line)', background: 'rgba(8,11,20,0.6)', flexShrink: 0 }}>
            {templates.map(tpl => (
              <button key={tpl.id} onClick={() => handleSwitchTemplate(tpl.id)} style={{
                flexShrink: 0, padding: '6px 12px', borderRadius: 8, border: 'none',
                background: currentId === tpl.id ? 'rgba(34,211,238,0.12)' : 'rgba(255,255,255,0.04)',
                color: currentId === tpl.id ? 'var(--accent)' : 'var(--fg-2)',
                fontSize: 12, fontWeight: 500, cursor: 'pointer', whiteSpace: 'nowrap',
              }}>{tpl.title || t('tpl.untitled')}</button>
            ))}
          </div>
        )}

        <div style={{
          flex: 1,
          display: 'grid',
          gridTemplateColumns: isCompact ? '1fr' : (tplSidebarW + ' 1fr 1fr'),
          overflow: 'hidden',
        }}>

          {/* ── Left: template list (desktop only) ── */}
          {!isCompact && (
            <aside style={{ borderRight: '1px solid var(--line)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
              <div style={{ padding: '20px 16px 14px', flexShrink: 0 }}>
                <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--fg-0)', marginBottom: 4 }}>{t('tpl.aiTemplates')}</div>
                <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>{t('tpl.instructions')}</div>
              </div>
              <div style={{ flex: 1, overflowY: 'auto', padding: '0 12px 16px' }}>
                {templates.map(t => (
                  <TplSidebarCard key={t.id} tpl={t} active={t.id === currentId} onClick={() => handleSwitchTemplate(t.id)} />
                ))}
                {templates.length === 0 && <div style={{ padding: '40px 16px', textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>{t('tpl.noTemplates')}</div>}
              </div>
            </aside>
          )}

          {/* ── Middle: template detail + upload + create ── */}
          <div style={{ borderRight: isCompact ? 'none' : '1px solid var(--line)', overflowY: 'auto', borderBottom: isCompact ? '1px solid var(--line)' : 'none' }}>
            {loading ? (
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: isCompact ? 200 : '100%' }}><div className="yt-spinner" style={{ width: 28, height: 28 }} /></div>
            ) : !tpl ? (
              <EmptyState icon="layers" title={t('tpl.selectTemplate')} />
            ) : (
              <div style={{ padding: isCompact ? '20px 16px 32px' : '28px 32px 60px' }}>
                <div style={{ fontSize: isCompact ? 17 : 20, fontWeight: 700, color: 'var(--fg-0)', marginBottom: 6 }}>{tpl.title || t('tpl.untitled')}</div>
                {tpl.prompt && <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 20, lineHeight: 1.5, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{tpl.prompt}</div>}

                {/* Preview */}
                <div style={{ borderRadius: 12, overflow: 'hidden', marginBottom: 24, background: '#06080f', border: '1px solid var(--line)' }}>
                  {isVideo && previewSrc ? (
                    <video src={previewSrc} muted playsInline controls preload="metadata" style={{ width: '100%', display: 'block', maxHeight: isCompact ? 220 : 360 }} />
                  ) : previewSrc ? (
                    <img src={previewSrc} alt="" style={{ width: '100%', display: 'block', maxHeight: isCompact ? 220 : 360, objectFit: 'contain' }} />
                  ) : null}
                </div>

                {/* Image slots — fixed positions matching original task */}
                {imageSlots.length > 0 && (
                  <div style={{ marginBottom: 24 }}>
                    <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--fg-0)', marginBottom: 4 }}>{t('tpl.replaceImages')}</div>
                    <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 12 }}>{t('tpl.replaceInstructions')}</div>
                    <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
                      {imageSlots.map((slot, idx) => (
                        <div key={idx} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
                          {/* Original image (faded) + user image overlay */}
                          <div onClick={() => setPickerSlotIdx(idx)} style={{
                            position: 'relative', width: isCompact ? 76 : 88, height: isCompact ? 76 : 88, borderRadius: 10, overflow: 'hidden', cursor: 'pointer',
                            border: slot.user_url ? '2px solid var(--accent)' : '2px dashed var(--line-2)',
                            transition: 'border-color .12s',
                          }}
                            onMouseEnter={e => { if (!slot.user_url) e.currentTarget.style.borderColor = 'var(--accent)'; }}
                            onMouseLeave={e => { if (!slot.user_url) e.currentTarget.style.borderColor = 'var(--line-2)'; }}>
                            {slot.user_url ? (
                              <img src={slot.user_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                            ) : (
                              <div style={{ width: '100%', height: '100%', position: 'relative', background: 'rgba(255,255,255,0.03)' }}>
                                {slot.original_url && (_isVidUrl(slot.original_url)
                                  ? <video src={slot.original_url} muted playsInline preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', opacity: 0.3, filter: 'grayscale(0.5)' }} />
                                  : <img src={slot.original_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', opacity: 0.3, filter: 'grayscale(0.5)' }} onError={e => e.target.style.display = 'none'} />
                                )}
                                <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 2, color: 'var(--fg-3)' }}>
                                  <Icon name="image" size={22} />
                                  <span style={{ fontSize: 10 }}>{t('tpl.clickSelect')}</span>
                                </div>
                              </div>
                            )}
                            {slot.user_url && (
                              <button onClick={e => { e.stopPropagation(); setImageSlots(prev => prev.map((s, i) => i === idx ? { ...s, user_url: null, user_name: null } : s)); }}
                                style={{ position: 'absolute', top: 3, right: 3, width: 18, height: 18, borderRadius: '50%', background: 'rgba(0,0,0,0.7)', border: 'none', color: '#fff', fontSize: 10, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>✕</button>
                            )}
                          </div>
                          <span style={{ fontSize: 11, color: 'var(--fg-3)', fontWeight: 500 }}>{slot.label}</span>
                        </div>
                      ))}
                    </div>
                    {pickerSlotIdx != null && <TplAssetPicker max={1} existing={0} onSelect={handleSlotPicked} onClose={() => setPickerSlotIdx(null)} />}
                  </div>
                )}

                {/* Params */}
                <div style={{ borderRadius: 10, border: '1px solid var(--line)', background: 'rgba(255,255,255,0.02)', padding: '14px 18px', marginBottom: 24 }}>
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px 20px' }}>
                    {(task?.model_name || tpl.model_name || task?.model || tpl.model_id) && <div><span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{t('tpl.model')}</span><div style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, marginTop: 2 }}>{task?.model_name || tpl.model_name || 'AI Model'}</div></div>}
                    {mp.aspect_ratio && <div><span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{t('tpl.aspectRatio')}</span><div style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, marginTop: 2 }}>{mp.aspect_ratio}</div></div>}
                    {mp.resolution && <div><span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{t('tpl.resolution')}</span><div style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, marginTop: 2 }}>{mp.resolution}</div></div>}
                    {mp.duration && <div><span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{t('tpl.duration')}</span><div style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, marginTop: 2 }}>{mp.duration}s</div></div>}
                  </div>
                </div>

                {error && <div style={{ padding: '10px 14px', borderRadius: 8, background: 'rgba(248,113,113,0.1)', border: '1px solid rgba(248,113,113,0.3)', fontSize: 13, color: '#fca5a5', marginBottom: 16 }}>{error}</div>}

                <button disabled={creating} onClick={handleCreate} style={{
                  width: '100%', height: 46, borderRadius: 10, border: 'none',
                  background: 'linear-gradient(135deg, var(--accent), var(--accent-2))',
                  color: '#052028', fontSize: 15, fontWeight: 700, cursor: creating ? 'not-allowed' : 'pointer',
                  opacity: creating ? 0.6 : 1, boxShadow: '0 1px 0 rgba(255,255,255,.3) inset, 0 6px 16px rgba(34,211,238,.2)',
                }}>
                  {creating ? t('tpl.submitting') : <>{t('tpl.create')} {tplData?.credits > 0 && <span style={{ opacity: 0.7, fontSize: 12, fontWeight: 500 }}>· {tplData.credits} {t('common.credits')}</span>}</>}
                </button>
              </div>
            )}
          </div>

          {/* ── Right: task results ── */}
          <main style={{ overflowY: 'auto', padding: isCompact ? '20px 16px' : '24px 32px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
              <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--fg-0)' }}>{t('tpl.creationHistory')}</div>
              <div style={{ display: 'flex', gap: 4 }}>
                {[
                  { value: 'current', label: t('tpl.filterCurrent') },
                  { value: 'all', label: t('tpl.filterAll') },
                ].map(f => (
                  <button key={f.value} onClick={() => setTaskFilter(f.value)}
                    className={`yt-pill ${taskFilter === f.value ? 'is-active' : ''}`}
                    style={{ height: 28, fontSize: 12 }}>
                    {f.label}
                  </button>
                ))}
              </div>
            </div>
            {tasks.length > 0 ? (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
                {tasks.map(t => (
                  <TplTaskCard key={t.id} task={t} isCurrent={t.template_id === currentId} onDelete={handleDeleteTask} onPublish={handlePublish} onUnpublish={handleUnpublish} />
                ))}
              </div>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '60px 0', gap: 12 }}>
                <div style={{ width: 64, height: 64, borderRadius: 14, background: 'rgba(255,255,255,0.03)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="layers" size={28} color="var(--fg-3)" />
                </div>
                <div style={{ fontSize: 13, color: 'var(--fg-3)' }}>{t('tpl.emptyHistory')}</div>
              </div>
            )}
          </main>
        </div>
      </div>
    </AppShell>
  );
};

Object.assign(window, { TemplateUsageScreen });
