import React, { useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import type { CBNode, CbTemplate, CbTemplateWithData, PageData } from '../types';
import type { EditorStore } from '../store/editorStore';
import { useApi } from '../hooks/useApi';
import { NodeWire, TEMPLATE_CATEGORIES } from './SaveTemplateModal';

// ─────────────────────────────────────────────────────────────────────────────
// Constants
// ─────────────────────────────────────────────────────────────────────────────

const BROWSE_CATEGORIES = [
  { slug: '', label: 'All' },
  ...TEMPLATE_CATEGORIES,
];

const CATEGORY_GRADIENTS: Record<string, string> = {
  'landing-pages':  'linear-gradient(135deg,#667eea 0%,#764ba2 100%)',
  'hero-sections':  'linear-gradient(135deg,#4facfe 0%,#00f2fe 100%)',
  'cta-blocks':     'linear-gradient(135deg,#f093fb 0%,#f5576c 100%)',
  'features':       'linear-gradient(135deg,#43e97b 0%,#38f9d7 100%)',
  'testimonials':   'linear-gradient(135deg,#fa709a 0%,#fee140 100%)',
  'pricing-tables': 'linear-gradient(135deg,#a18cd1 0%,#fbc2eb 100%)',
  'headers':        'linear-gradient(135deg,#667eea 0%,#764ba2 100%)',
  'footers':        'linear-gradient(135deg,#30cfd0 0%,#330867 100%)',
  '':               'linear-gradient(135deg,#1a1a1a 0%,#444444 100%)',
};

// ─────────────────────────────────────────────────────────────────────────────
// Props
// ─────────────────────────────────────────────────────────────────────────────

interface Props {
  store: EditorStore;
  onClose: () => void;
  onToast: (msg: string) => void;
  onDragStart: (templateId: number | string) => void;
  onDragEnd: () => void;
}

type LibraryView = 'browse' | 'save' | 'import';

// ─────────────────────────────────────────────────────────────────────────────
// Tiny SVG icons
// ─────────────────────────────────────────────────────────────────────────────

const SearchIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
  </svg>
);
const EyeIcon = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>
  </svg>
);
const DownloadIcon = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
  </svg>
);
const CopyIcon = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
    <rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
  </svg>
);
const TrashIcon = () => (
  <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
    <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
    <path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
  </svg>
);
const UploadIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
    <polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>
  </svg>
);
const CloseIcon = () => (
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
    <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
  </svg>
);

// ─────────────────────────────────────────────────────────────────────────────
// Template Preview Modal
// ─────────────────────────────────────────────────────────────────────────────

interface PreviewModalProps {
  templateId: number | string;
  onClose: () => void;
  onInsert: (id: number | string) => void;
}

function TemplatePreviewModal({ templateId, onClose, onInsert }: PreviewModalProps) {
  const api = useApi();
  const [tpl, setTpl] = useState<CbTemplateWithData | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    api.getTemplate(templateId).then(setTpl).finally(() => setLoading(false));
  }, [templateId]);

  useEffect(() => {
    const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', h);
    return () => document.removeEventListener('keydown', h);
  }, [onClose]);

  const gradient = tpl ? (CATEGORY_GRADIENTS[tpl.category] ?? CATEGORY_GRADIENTS['']) : CATEGORY_GRADIENTS[''];

  const previewNodes: CBNode[] = (() => {
    if (!tpl) return [];
    if (tpl.type === 'page') return (tpl.data as PageData).sections ?? [];
    return [tpl.data as CBNode];
  })();

  return createPortal(
    <div className="jalpi-tpl-prev-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="jalpi-tpl-prev" role="dialog" aria-label="Template preview">

        <div className="jalpi-tpl-prev__head">
          {loading ? (
            <span className="jalpi-tpl-prev__title">Loading…</span>
          ) : (
            <>
              <div className="jalpi-tpl-prev__title-wrap">
                <span className="jalpi-tpl-prev__title">{tpl?.name}</span>
                <div className="jalpi-tpl-prev__badges">
                  {tpl && (
                    <span className={`jalpi-tpl-card__type jalpi-tpl-card__type--${tpl.type}`}>{tpl.type}</span>
                  )}
                  {tpl?.category_label && (
                    <span className="jalpi-tpl-prev__cat">{tpl.category_label}</span>
                  )}
                </div>
                {tpl?.description && (
                  <p className="jalpi-tpl-prev__desc">{tpl.description}</p>
                )}
              </div>
            </>
          )}
          <button className="jalpi-tpl-prev__close" onClick={onClose} aria-label="Close preview"><CloseIcon /></button>
        </div>

        <div className="jalpi-tpl-prev__body">
          {/* Thumbnail band */}
          <div className="jalpi-tpl-prev__thumb" style={{ background: gradient }}>
            {tpl?.thumbnail && <img src={tpl.thumbnail} alt={tpl.name} />}
            {!tpl?.thumbnail && (
              <div className="jalpi-tpl-prev__thumb-icon">
                <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.7)" strokeWidth="1.5">
                  <rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
                  <rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
                </svg>
              </div>
            )}
          </div>

          {/* Structure tree */}
          <div className="jalpi-tpl-prev__tree">
            <div className="jalpi-tpl-prev__tree-label">Structure</div>
            {loading ? (
              <div className="jalpi-spinner" style={{ margin: '20px auto' }} />
            ) : previewNodes.length > 0 ? (
              previewNodes.map((node) => <NodeWire key={node.id} node={node} />)
            ) : (
              <p style={{ fontSize: 12, color: '#9ca3af' }}>No content</p>
            )}
          </div>
        </div>

        <div className="jalpi-tpl-prev__footer">
          <button className="jalpi-tpl-btn-secondary" onClick={onClose}>Close</button>
          <button
            className="jalpi-tpl-btn-primary"
            onClick={() => { onInsert(templateId); onClose(); }}
            disabled={loading}
          >
            Insert Template
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Main component
// ─────────────────────────────────────────────────────────────────────────────

export function TemplateLibrary({ store, onClose, onToast, onDragStart, onDragEnd }: Props) {
  const api = useApi();

  const [view, setView] = useState<LibraryView>('browse');

  // Browse
  const [templates,      setTemplates]      = useState<CbTemplate[]>([]);
  const [loading,        setLoading]        = useState(true);
  const [search,         setSearch]         = useState('');
  const [activeCategory, setActiveCategory] = useState('');
  const [activeType,     setActiveType]     = useState<'' | 'page' | 'section' | 'element'>('');
  const [previewId,      setPreviewId]      = useState<number | string | null>(null);
  const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Filtering / presentation
  const [activeTag,   setActiveTag]   = useState('');
  const [allTags,     setAllTags]     = useState<string[]>([]);
  const [catCounts,   setCatCounts]   = useState<Record<string, number>>({});
  const [viewMode,    setViewMode]    = useState<'grid' | 'list'>('grid');
  const [scope,       setScope]       = useState<'' | 'favorites' | 'recent'>('');

  // Per-user lists (persisted server-side, so they follow the user across
  // browsers rather than living in localStorage).
  const [favorites, setFavorites] = useState<string[]>([]);

  // Infinite scroll
  const PER_PAGE = 24;
  const [page,     setPage]     = useState(1);
  const [total,    setTotal]    = useState(0);
  const [hasMore,  setHasMore]  = useState(false);
  const [loadingMore, setLoadingMore] = useState(false);
  const sentinelRef = useRef<HTMLDivElement | null>(null);

  // Save form
  const [saveName,     setSaveName]     = useState('');
  const [saveDesc,     setSaveDesc]     = useState('');
  const [saveCategory, setSaveCategory] = useState('');
  const [saveType,     setSaveType]     = useState<'page' | 'section' | 'element'>('section');
  const [saveError,    setSaveError]    = useState('');
  const [saveBusy,     setSaveBusy]     = useState(false);

  // Import form
  const [importJson,  setImportJson]  = useState('');
  const [importName,  setImportName]  = useState('');
  const [importError, setImportError] = useState('');
  const [importBusy,  setImportBusy]  = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // ── Load ──────────────────────────────────────────────────────────────────

  const loadTemplates = useCallback(async (nextPage = 1, append = false) => {
    if (append) setLoadingMore(true); else setLoading(true);
    try {
      const data = await api.getTemplates({
        search,
        category: activeCategory,
        type: activeType,
        tag: activeTag,
        source: scope,
        page: nextPage,
        per_page: PER_PAGE,
      });
      setTemplates((prev) => (append ? [...prev, ...data.items] : data.items));
      setTotal(data.total);
      setPage(data.page);
      setHasMore(data.page < data.pages);
    } catch {
      // keep stale list on failure
    } finally {
      setLoading(false);
      setLoadingMore(false);
    }
  }, [api, search, activeCategory, activeType, activeTag, scope]);

  // Category counts + the tag vocabulary, loaded once.
  const loadMeta = useCallback(async () => {
    try {
      const meta = await api.getTemplateCategories();
      const counts: Record<string, number> = {};
      meta.categories.forEach((c) => { counts[c.slug] = c.count; });
      setCatCounts(counts);
      setAllTags(meta.tags || []);
    } catch {
      // non-fatal: the library still works without counts
    }
  }, [api]);

  const loadUserLists = useCallback(async () => {
    try {
      const lists = await api.getTemplateUserLists();
      setFavorites(lists.favorites || []);
    } catch {
      // non-fatal
    }
  }, [api]);

  const isFavorite = useCallback(
    (id: number | string) => favorites.includes(String(id)),
    [favorites]
  );

  const handleToggleFavorite = useCallback(async (id: number | string) => {
    // Optimistic, so the star responds instantly.
    const key = String(id);
    setFavorites((prev) => (prev.includes(key) ? prev.filter((x) => x !== key) : [key, ...prev]));
    try {
      const res = await api.toggleTemplateFavorite(id);
      setFavorites(res.favorites);
    } catch {
      loadUserLists(); // reconcile with the server on failure
    }
  }, [api, loadUserLists]);

  useEffect(() => { loadTemplates(1, false); loadMeta(); loadUserLists(); }, []);

  // Any filter change resets to page 1. Search is debounced; the rest are not,
  // so a category click feels immediate.
  useEffect(() => {
    if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current);
    searchTimeoutRef.current = setTimeout(() => loadTemplates(1, false), 300);
    return () => { if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current); };
  }, [search]);

  useEffect(() => { loadTemplates(1, false); }, [activeCategory, activeType, activeTag, scope]);

  // Infinite scroll: load the next page when the sentinel scrolls into view.
  useEffect(() => {
    const el = sentinelRef.current;
    if (!el || !hasMore || loading || loadingMore) return;
    if (typeof IntersectionObserver === 'undefined') return;
    const io = new IntersectionObserver((entries) => {
      if (entries.some((e) => e.isIntersecting)) loadTemplates(page + 1, true);
    }, { rootMargin: '240px' });
    io.observe(el);
    return () => io.disconnect();
  }, [hasMore, loading, loadingMore, page, loadTemplates]);

  // Close on Escape
  useEffect(() => {
    const h = (e: KeyboardEvent) => { if (e.key === 'Escape' && !previewId) onClose(); };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [onClose, previewId]);

  // ── Actions ───────────────────────────────────────────────────────────────

  const handleInsert = useCallback(async (id: number | string) => {
    try {
      const tpl = await api.getTemplate(id);
      // Fire-and-forget: the insert should not wait on the recents write.
      api.pushTemplateRecent(id).catch(() => {});
      if (tpl.type === 'page') {
        store.loadTemplateAsPage((tpl.data as PageData).sections ?? []);
        onToast('Page template loaded');
      } else if (tpl.type === 'element') {
        (store as any).insertElementTemplate(tpl.data as CBNode);
        onToast('Element inserted');
      } else {
        store.insertTemplateSection(tpl.data as CBNode);
        onToast('Section inserted');
      }
    } catch (err) {
      onToast(err instanceof Error ? err.message : 'Insert failed');
    }
  }, [api, store, onToast]);

  const handleDuplicate = useCallback(async (id: number | string) => {
    if (String(id).startsWith('kit:')) return; // read-only
    try {
      await api.duplicateTemplate(id);
      await loadTemplates();
      onToast('Template duplicated');
    } catch (err) {
      onToast(err instanceof Error ? err.message : 'Duplicate failed');
    }
  }, [api, loadTemplates, onToast]);

  const handleExport = useCallback(async (id: number | string, name: string) => {
    if (String(id).startsWith('kit:')) return; // read-only
    try {
      const data = await api.exportTemplate(id);
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
      const url  = URL.createObjectURL(blob);
      const a    = document.createElement('a');
      a.href = url; a.download = `${name.replace(/\s+/g, '-').toLowerCase()}.json`; a.click();
      URL.revokeObjectURL(url);
      onToast('Template exported');
    } catch (err) {
      onToast(err instanceof Error ? err.message : 'Export failed');
    }
  }, [api, onToast]);

  const handleDelete = useCallback(async (id: number | string) => {
    if (String(id).startsWith('kit:')) return; // read-only
    if (!window.confirm('Delete this template? This cannot be undone.')) return;
    try {
      await api.deleteTemplate(id);
      setTemplates((prev) => prev.filter((t) => t.id !== id));
      onToast('Template deleted');
    } catch (err) {
      onToast(err instanceof Error ? err.message : 'Delete failed');
    }
  }, [api, onToast]);

  // ── Save form ──────────────────────────────────────────────────────────────

  const handleSave = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    setSaveError('');
    if (!saveName.trim()) { setSaveError('Name is required'); return; }

    let data: unknown;
    if (saveType === 'page') {
      data = store.state.pageData;
    } else {
      const sel = store.getSelectedNode();
      if (!sel) {
        setSaveError(
          saveType === 'section'
            ? 'Select a section in the canvas first.'
            : 'Select an element in the canvas first.',
        );
        return;
      }
      data = sel;
    }

    setSaveBusy(true);
    try {
      await api.createTemplate({ name: saveName.trim(), description: saveDesc, category: saveCategory, type: saveType, data });
      onToast('Template saved');
      setSaveName(''); setSaveDesc(''); setSaveCategory(''); setSaveType('section');
      setView('browse');
      loadTemplates();
    } catch (err) {
      setSaveError(err instanceof Error ? err.message : 'Save failed');
    } finally {
      setSaveBusy(false);
    }
  }, [api, store, saveName, saveDesc, saveCategory, saveType, onToast, loadTemplates]);

  // ── Import form ────────────────────────────────────────────────────────────

  const handleFileChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => { setImportJson((ev.target?.result as string) ?? ''); };
    reader.readAsText(file);
  }, []);

  const handleImport = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    setImportError('');
    if (!importJson.trim()) { setImportError('Paste or upload a JSON file first.'); return; }
    setImportBusy(true);
    try {
      await api.importTemplate(importJson.trim(), importName.trim() || undefined);
      onToast('Template imported');
      setImportJson(''); setImportName('');
      setView('browse');
      loadTemplates();
    } catch (err) {
      setImportError(err instanceof Error ? err.message : 'Import failed');
    } finally {
      setImportBusy(false);
    }
  }, [api, importJson, importName, onToast, loadTemplates]);

  // ── Selected node info for save hint ─────────────────────────────────────

  const selectedNode = store.getSelectedNode();
  const selectedType = selectedNode?.type ?? null;

  // ─────────────────────────────────────────────────────────────────────────
  // Render
  // ─────────────────────────────────────────────────────────────────────────

  return (
    <div className="jalpi-tpl-modal-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="jalpi-tpl-modal" role="dialog" aria-label="Template Library">

        {/* Header */}
        <div className="jalpi-tpl-header">
          <h2 className="jalpi-tpl-header__title">Template Library</h2>
          <div className="jalpi-tpl-header__tabs">
            {(['browse', 'save', 'import'] as LibraryView[]).map((v) => (
              <button
                key={v}
                className={`jalpi-tpl-header__tab${view === v ? ' is-active' : ''}`}
                onClick={() => setView(v)}
              >
                {v === 'browse' ? 'Browse' : v === 'save' ? 'Save New' : 'Import'}
              </button>
            ))}
          </div>
          <button className="jalpi-tpl-close" onClick={onClose} title="Close (Esc)">×</button>
        </div>

        {/* ── Browse ── */}
        {view === 'browse' && (
          <div className="jalpi-tpl-browse">
            <div className="jalpi-tpl-filters">
              <div className="jalpi-tpl-search">
                <span className="jalpi-tpl-search__icon"><SearchIcon /></span>
                <input
                  type="text"
                  placeholder="Search templates…"
                  value={search}
                  onChange={(e) => setSearch(e.target.value)}
                />
              </div>

              <select
                className="jalpi-tpl-type-select"
                value={activeType}
                onChange={(e) => setActiveType(e.target.value as '' | 'page' | 'section' | 'element')}
              >
                <option value="">All types</option>
                <option value="page">Pages</option>
                <option value="section">Sections</option>
                <option value="element">Elements</option>
              </select>

              {/* Grid / list toggle */}
              <div className="jalpi-tpl-viewmode" role="group" aria-label="View mode">
                <button
                  type="button"
                  className={`jalpi-tpl-viewmode__btn${viewMode === 'grid' ? ' is-active' : ''}`}
                  onClick={() => setViewMode('grid')}
                  aria-pressed={viewMode === 'grid'}
                  title="Grid view"
                >
                  <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="1.8">
                    <rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" />
                    <rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
                  </svg>
                </button>
                <button
                  type="button"
                  className={`jalpi-tpl-viewmode__btn${viewMode === 'list' ? ' is-active' : ''}`}
                  onClick={() => setViewMode('list')}
                  aria-pressed={viewMode === 'list'}
                  title="List view"
                >
                  <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="1.8">
                    <line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" />
                  </svg>
                </button>
              </div>

              {/* Scope: everything / favourites / recently used */}
              <div className="jalpi-tpl-scope" role="group" aria-label="Scope">
                {([['', 'All'], ['favorites', 'Favorites'], ['recent', 'Recently Used']] as const).map(([val, label]) => (
                  <button
                    key={val || 'all'}
                    type="button"
                    className={`jalpi-tpl-scope__btn${scope === val ? ' is-active' : ''}`}
                    onClick={() => { setScope(val); setActiveCategory(''); }}
                    aria-pressed={scope === val}
                  >
                    {label}
                  </button>
                ))}
              </div>

              <div className="jalpi-tpl-cats">
                <button
                  className={`jalpi-tpl-cat-btn${activeCategory === '' ? ' is-active' : ''}`}
                  onClick={() => setActiveCategory('')}
                >
                  All
                  <span className="jalpi-tpl-cat-btn__count">{total}</span>
                </button>
                {BROWSE_CATEGORIES.filter((c) => c.slug !== '').map((cat) => (
                  <button
                    key={cat.slug}
                    className={`jalpi-tpl-cat-btn${activeCategory === cat.slug ? ' is-active' : ''}`}
                    onClick={() => setActiveCategory(cat.slug)}
                  >
                    {cat.label}
                    {catCounts[cat.slug] > 0 && (
                      <span className="jalpi-tpl-cat-btn__count">{catCounts[cat.slug]}</span>
                    )}
                  </button>
                ))}
              </div>

              {/* Tag filter */}
              {allTags.length > 0 && (
                <div className="jalpi-tpl-tags">
                  <span className="jalpi-tpl-tags__label">Tags</span>
                  {activeTag && (
                    <button className="jalpi-tpl-tag is-active" onClick={() => setActiveTag('')}>
                      {activeTag} ✕
                    </button>
                  )}
                  {!activeTag && allTags.slice(0, 14).map((t) => (
                    <button key={t} className="jalpi-tpl-tag" onClick={() => setActiveTag(t)}>
                      {t}
                    </button>
                  ))}
                </div>
              )}
            </div>

            <div className="jalpi-tpl-grid-wrap">
              {loading ? (
                <div className="jalpi-tpl-loading"><div className="jalpi-spinner" /></div>
              ) : templates.length === 0 ? (
                <div className="jalpi-tpl-empty">
                  <svg className="jalpi-tpl-empty__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
                    <rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
                    <rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
                  </svg>
                  <p>{search || activeCategory || activeType ? 'No templates match your search.' : 'No templates yet. Save your first one!'}</p>
                  {!search && !activeCategory && !activeType && (
                    <button className="jalpi-tpl-btn-primary" style={{ marginTop: 14 }} onClick={() => setView('save')}>
                      Save current page
                    </button>
                  )}
                </div>
              ) : (
                <>
                  <div className={viewMode === 'list' ? 'jalpi-tpl-grid is-list' : 'jalpi-tpl-grid'}>
                    {templates.map((tpl) => (
                      <TemplateCard
                        key={String(tpl.id)}
                        template={tpl}
                        onInsert={handleInsert}
                        onPreview={(id) => setPreviewId(id)}
                        onDuplicate={handleDuplicate}
                        onExport={handleExport}
                        onDelete={handleDelete}
                        onDragStart={onDragStart}
                        onDragEnd={onDragEnd}
                        favorited={isFavorite(tpl.id)}
                        onToggleFavorite={handleToggleFavorite}
                        onTagClick={setActiveTag}
                      />
                    ))}
                  </div>

                  {/* Infinite scroll sentinel */}
                  {hasMore && (
                    <div ref={sentinelRef} className="jalpi-tpl-sentinel">
                      {loadingMore && <div className="jalpi-spinner" />}
                    </div>
                  )}
                  {!hasMore && templates.length > 0 && (
                    <p className="jalpi-tpl-endcap">
                      Showing all {total} template{total === 1 ? '' : 's'}
                    </p>
                  )}
                </>
              )}
            </div>
          </div>
        )}

        {/* ── Save ── */}
        {view === 'save' && (
          <form className="jalpi-tpl-form" onSubmit={handleSave}>
            <h3 className="jalpi-tpl-form__title">Save as Template</h3>

            {saveError && <div className="jalpi-tpl-error">{saveError}</div>}

            {/* Type */}
            <div className="jalpi-tpl-field">
              <label>Type</label>
              <div className="jalpi-tpl-radio-group">
                {([
                  { value: 'element', title: 'Element', desc: `Save selected element${selectedType ? ` (${selectedType})` : ''}` },
                  { value: 'section', title: 'Section', desc: 'Save selected section' },
                  { value: 'page',    title: 'Full Page', desc: 'Save the entire current page' },
                ] as const).map((t) => (
                  <label key={t.value} className={`jalpi-tpl-radio${saveType === t.value ? ' is-selected' : ''}`}>
                    <input
                      type="radio" name="saveType" value={t.value}
                      checked={saveType === t.value}
                      onChange={() => setSaveType(t.value)}
                    />
                    <div>
                      <div className="jalpi-tpl-radio__label">{t.title}</div>
                      <div className="jalpi-tpl-radio__desc">{t.desc}</div>
                    </div>
                  </label>
                ))}
              </div>
            </div>

            {/* Selection hint */}
            {saveType !== 'page' && !selectedNode && (
              <div className="jalpi-tpl-error" style={{ marginBottom: 8 }}>
                No element selected. Click something in the canvas first.
              </div>
            )}

            {/* Name */}
            <div className="jalpi-tpl-field">
              <label htmlFor="jalpi-tpl-save-name">Name *</label>
              <input id="jalpi-tpl-save-name" type="text" placeholder="Template name"
                value={saveName} onChange={(e) => setSaveName(e.target.value)} required />
            </div>

            {/* Description */}
            <div className="jalpi-tpl-field">
              <label htmlFor="jalpi-tpl-save-desc">Description</label>
              <textarea id="jalpi-tpl-save-desc" placeholder="Optional description" rows={2}
                value={saveDesc} onChange={(e) => setSaveDesc(e.target.value)} />
            </div>

            {/* Category */}
            <div className="jalpi-tpl-field">
              <label htmlFor="jalpi-tpl-save-cat">Category</label>
              <select id="jalpi-tpl-save-cat" value={saveCategory} onChange={(e) => setSaveCategory(e.target.value)}>
                <option value="">Uncategorised</option>
                {TEMPLATE_CATEGORIES.map((c) => (
                  <option key={c.slug} value={c.slug}>{c.label}</option>
                ))}
              </select>
            </div>

            <div className="jalpi-tpl-submit-row">
              <button type="button" className="jalpi-tpl-btn-secondary" onClick={() => setView('browse')}>Cancel</button>
              <button type="submit" className="jalpi-tpl-btn-primary" disabled={saveBusy}>
                {saveBusy ? 'Saving…' : 'Save Template'}
              </button>
            </div>
          </form>
        )}

        {/* ── Import ── */}
        {view === 'import' && (
          <form className="jalpi-tpl-form" onSubmit={handleImport}>
            <h3 className="jalpi-tpl-form__title">Import Template</h3>

            {importError && <div className="jalpi-tpl-error">{importError}</div>}

            <div className="jalpi-tpl-field">
              <label>Upload JSON file</label>
              <button type="button" className="jalpi-tpl-btn-secondary"
                style={{ display: 'flex', alignItems: 'center', gap: 6 }}
                onClick={() => fileInputRef.current?.click()}>
                <UploadIcon /> Choose file
              </button>
              <input ref={fileInputRef} type="file" accept=".json,application/json"
                onChange={handleFileChange} style={{ display: 'none' }} />
            </div>

            <div className="jalpi-tpl-import-or">or paste JSON</div>

            <div className="jalpi-tpl-field">
              <label htmlFor="jalpi-tpl-import-json">JSON</label>
              <textarea id="jalpi-tpl-import-json" placeholder={'{\n  "type": "section",\n  "data": {...}\n}'}
                value={importJson} onChange={(e) => setImportJson(e.target.value)} rows={8} />
            </div>

            <div className="jalpi-tpl-field">
              <label htmlFor="jalpi-tpl-import-name">Override name (optional)</label>
              <input id="jalpi-tpl-import-name" type="text" placeholder="Leave blank to use name from JSON"
                value={importName} onChange={(e) => setImportName(e.target.value)} />
            </div>

            <div className="jalpi-tpl-submit-row">
              <button type="button" className="jalpi-tpl-btn-secondary" onClick={() => setView('browse')}>Cancel</button>
              <button type="submit" className="jalpi-tpl-btn-primary" disabled={importBusy}>
                {importBusy ? 'Importing…' : 'Import'}
              </button>
            </div>
          </form>
        )}
      </div>

      {/* Preview modal */}
      {previewId !== null && (
        <TemplatePreviewModal
          templateId={previewId}
          onClose={() => setPreviewId(null)}
          onInsert={handleInsert}
        />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// TemplateCard
// ─────────────────────────────────────────────────────────────────────────────

interface CardProps {
  template: CbTemplate;
  onInsert: (id: number | string) => void;
  onPreview: (id: number | string) => void;
  onDuplicate: (id: number | string) => void;
  onExport: (id: number | string, name: string) => void;
  onDelete: (id: number | string) => void;
  onDragStart: (id: number | string) => void;
  onDragEnd: () => void;
  favorited: boolean;
  onToggleFavorite: (id: number | string) => void;
  onTagClick: (tag: string) => void;
}

function TemplateCard({
  template, onInsert, onPreview, onDuplicate, onExport, onDelete,
  onDragStart, onDragEnd, favorited, onToggleFavorite, onTagClick,
}: CardProps) {
  const gradient = CATEGORY_GRADIENTS[template.category] ?? CATEGORY_GRADIENTS[''];
  // Starter-Site kit templates are read-only: they live in JSON files, so
  // duplicate / export / delete do not apply to them.
  const isKit = template.source === 'kit' || String(template.id).startsWith('kit:');

  return (
    <div
      className="jalpi-tpl-card"
      draggable
      onDragStart={() => onDragStart(template.id)}
      onDragEnd={onDragEnd}
      title={`Drag to canvas to insert • ${template.description || template.name}`}
    >
      <div className="jalpi-tpl-card__thumb" onClick={() => onPreview(template.id)}>
        {template.thumbnail ? (
          <img src={template.thumbnail} alt={template.name} />
        ) : (
          <div className="jalpi-tpl-card__placeholder" style={{ background: gradient }}>
            <div className="jalpi-tpl-card__placeholder-icon">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.8)" strokeWidth="1.75">
                <rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
                <rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
              </svg>
            </div>
          </div>
        )}
        <div className="jalpi-tpl-card__preview-hint">
          <EyeIcon /> Preview
        </div>
        <button
          type="button"
          className={`jalpi-tpl-card__fav${favorited ? ' is-on' : ''}`}
          onClick={(e) => { e.stopPropagation(); onToggleFavorite(template.id); }}
          title={favorited ? 'Remove from favorites' : 'Add to favorites'}
          aria-pressed={favorited}
          aria-label={favorited ? 'Remove from favorites' : 'Add to favorites'}
        >
          <svg viewBox="0 0 24 24" width="15" height="15"
               fill={favorited ? 'currentColor' : 'none'} stroke="currentColor" strokeWidth="1.8">
            <path d="M12 17.3l-5.6 3 1.1-6.3-4.6-4.4 6.3-.9L12 3l2.8 5.7 6.3.9-4.6 4.4 1.1 6.3z" />
          </svg>
        </button>
      </div>

      <div className="jalpi-tpl-card__info">
        <p className="jalpi-tpl-card__name" title={template.name}>{template.name}</p>
        <div className="jalpi-tpl-card__meta">
          <span className={`jalpi-tpl-card__type jalpi-tpl-card__type--${template.type}`}>
            {template.type}
          </span>
          {template.category_label && (
            <span className="jalpi-tpl-card__cat">{template.category_label}</span>
          )}
        </div>
        {!!template.tags?.length && (
          <div className="jalpi-tpl-card__tags">
            {template.tags.slice(0, 3).map((t) => (
              <button
                key={t}
                type="button"
                className="jalpi-tpl-card__tag"
                onClick={(e) => { e.stopPropagation(); onTagClick(t); }}
                title={`Filter by "${t}"`}
              >
                {t}
              </button>
            ))}
          </div>
        )}
      </div>

      <div className="jalpi-tpl-card__actions">
        <button className="jalpi-tpl-card__action jalpi-tpl-card__action--primary"
          onClick={() => onInsert(template.id)} title="Insert into page">
          Insert
        </button>
        <button className="jalpi-tpl-card__action" onClick={() => onPreview(template.id)} title="Preview template">
          <EyeIcon />
        </button>
        {!isKit && (
          <>
            <button className="jalpi-tpl-card__action" onClick={() => onDuplicate(template.id)} title="Duplicate">
              <CopyIcon />
            </button>
            <button className="jalpi-tpl-card__action" onClick={() => onExport(template.id, template.name)} title="Export as JSON">
              <DownloadIcon />
            </button>
            <button className="jalpi-tpl-card__action jalpi-tpl-card__action--danger"
              onClick={() => onDelete(template.id)} title="Delete template">
              <TrashIcon />
            </button>
          </>
        )}
      </div>
    </div>
  );
}
