import React, { useEffect, useState, useCallback, useRef } from 'react';
import { useEditorStore } from './store/editorStore';
import { useApi } from './hooks/useApi';
import { useKeyboard } from './hooks/useKeyboard';
import { TopBar } from './components/toolbar/TopBar';
import { LayersPanel } from './components/layers/LayersPanel';
import { Canvas } from './components/canvas/Canvas';
import { PropertiesPanel } from './components/panels/PropertiesPanel';
import { ElementsPanel } from './components/panels/ElementsPanel';
import { RevisionPanel } from './components/RevisionPanel';
import { GlobalStylesPanel } from './components/globalStyles/GlobalStylesPanel';
import { ClassesPanel } from './components/classes/ClassesPanel';
import { SaveTemplateModal } from './components/SaveTemplateModal';
import { GlobalFinder } from './components/GlobalFinder';
import TimelinePanel from './components/panels/TimelinePanel';
import type { CBNode, DesignSystem, DesignToken } from './types';
import { TOKEN_CATEGORIES, cssVarName, typoVarName, btnVarName } from './utils/designTokens';

type InspectorTab = 'outline' | 'style' | 'page' | 'global' | 'classes';

// ── Icon helpers ────────────────────────────────────────────────────────────

const OutlineIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <rect x="2" y="3" width="7" height="7" rx="1"/><rect x="2" y="14" width="7" height="7" rx="1"/>
    <path d="M13 6.5h9M13 17.5h9"/>
  </svg>
);

const StyleIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <circle cx="13.5" cy="6.5" r="2.5"/><circle cx="6.5" cy="12" r="2.5"/><circle cx="13.5" cy="17.5" r="2.5"/>
    <path d="M16 6.5h6M9 12h11M16 17.5h6M2 6.5h6M2 12h1M2 17.5h6"/>
  </svg>
);

const PageIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
    <polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/>
    <polyline points="10 9 9 9 8 9"/>
  </svg>
);

const ClassesIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <path d="M20.59 13.41L13.42 20.58a2 2 0 0 1-2.83 0L2.5 12.5V2.5h10L20.59 10.6a2 2 0 0 1 0 2.82z"/>
    <line x1="7" y1="7" x2="7.01" y2="7"/>
  </svg>
);

const GlobalIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <circle cx="12" cy="12" r="10"/>
    <circle cx="12" cy="12" r="3"/>
    <line x1="12" y1="2" x2="12" y2="9"/>
    <line x1="12" y1="15" x2="12" y2="22"/>
    <line x1="2" y1="12" x2="9" y2="12"/>
    <line x1="15" y1="12" x2="22" y2="12"/>
  </svg>
);

const DuplicateIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <rect x="8" y="8" 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 CopyIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="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 DeleteIcon = () => (
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="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>
);

declare const jalpiEditorConfig: { postId: number; previewUrl: string; designSystem?: DesignSystem };

// ── Panel resize divider ──────────────────────────────────────────────────────

function PanelResizer({
  targetRef,
  direction,
  min = 180,
  max = 600,
  onCommit,
}: {
  targetRef:  React.RefObject<HTMLDivElement | HTMLElement>;
  direction:  'left' | 'right'; // which side the panel sits on
  min?:       number;
  max?:       number;
  onCommit:   (w: number) => void;
}) {
  const ref = useRef<HTMLDivElement>(null);

  const onMouseDown = (e: React.MouseEvent) => {
    e.preventDefault();
    const el = targetRef.current as HTMLElement | null;
    if (!el) return;
    const startX = e.clientX;
    const startW = el.offsetWidth;
    ref.current?.classList.add('is-active');
    document.body.style.cursor     = 'col-resize';
    document.body.style.userSelect = 'none';

    const onMove = (me: MouseEvent) => {
      // right panel grows when dragging left, left panel grows when dragging right
      const dx = direction === 'right' ? startX - me.clientX : me.clientX - startX;
      const w  = Math.max(min, Math.min(max, startW + dx));
      el.style.width       = `${w}px`;
      el.style.flexShrink  = '0';
    };

    const onUp = () => {
      ref.current?.classList.remove('is-active');
      const w = parseFloat((targetRef.current as HTMLElement)?.style.width) || startW;
      onCommit(w);
      document.body.style.removeProperty('cursor');
      document.body.style.removeProperty('user-select');
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseup',  onUp);
    };

    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseup',  onUp);
  };

  return <div ref={ref} className="jalpi-panel-resizer" onMouseDown={onMouseDown} />;
}

// ─────────────────────────────────────────────────────────────────────────────

// Flat tree walker — find any node by id
function findNodeById(id: string, nodes: CBNode[]): CBNode | null {
  for (const n of nodes) {
    if (n.id === id) return n;
    if (n.children?.length) {
      const found = findNodeById(id, n.children);
      if (found) return found;
    }
  }
  return null;
}

// Return the top-level section id that contains the given node id
function findParentSectionId(nodeId: string, sections: CBNode[]): string | undefined {
  for (const section of sections) {
    if (section.id === nodeId) return undefined; // the node IS a section
    if (findNodeById(nodeId, section.children ?? [])) return section.id;
  }
  return undefined;
}

// ── Page tab ─────────────────────────────────────────────────────────────────

function PagePanel({ postTitle }: { postTitle: string }) {
  return (
    <div className="jalpi-page-panel">
      <div className="jalpi-page-panel__section">
        <div className="jalpi-page-panel__label">Page Title</div>
        <div className="jalpi-page-panel__value">{postTitle || 'Untitled'}</div>
      </div>
      <div className="jalpi-page-panel__section">
        <div className="jalpi-page-panel__label">Post Type</div>
        <div className="jalpi-page-panel__value">Page</div>
      </div>
      <div className="jalpi-page-panel__hint">
        Edit page settings in the WordPress editor.
      </div>
    </div>
  );
}

// ── Preset Linker ────────────────────────────────────────────────────────────
// Compact bar shown in the inspector when a typography/button element is selected.

function PresetLinker({ store, node }: { store: ReturnType<typeof useEditorStore>; node: CBNode }) {
  // Typography/button composite presets now live in the site-wide Design
  // System (store.state.designSystem), not the legacy per-page
  // pageData.globalStyles — the restructured GlobalStylesPanel only edits
  // the former, so linking must resolve against the same source.
  const gs = store.state.designSystem;
  const ref = node.globalStyleRef;
  const isTypo = ['heading', 'text'].includes(node.type);
  const isBtn  = node.type === 'button';

  if (!isTypo && !isBtn) return null;
  if (!gs) return null;

  if (isTypo) {
    const linked = ref?.typographyId ? gs.typography.find((t) => t.id === ref.typographyId) : null;
    return (
      <div className="jalpi-preset-linker">
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>
          <line x1="12" y1="2" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="22"/>
          <line x1="2" y1="12" x2="9" y2="12"/><line x1="15" y1="12" x2="22" y2="12"/>
        </svg>
        <span className="jalpi-preset-linker__label">Typography Preset</span>
        <select
          className="jalpi-preset-linker__select"
          value={ref?.typographyId ?? ''}
          onChange={(e) => store.linkNodeToPreset(node.id, e.target.value ? { typographyId: e.target.value } : undefined)}
        >
          <option value="">— None —</option>
          {gs.typography.map((t) => <option key={t.id} value={t.id}>{t.name}</option>)}
        </select>
        {linked && (
          <button
            className="jalpi-preset-linker__unlink"
            onClick={() => store.linkNodeToPreset(node.id, undefined)}
            title="Remove preset link"
          >
            <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
          </button>
        )}
      </div>
    );
  }

  const linked = ref?.buttonId ? gs.buttons.find((b) => b.id === ref.buttonId) : null;
  return (
    <div className="jalpi-preset-linker">
      <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>
        <line x1="12" y1="2" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="22"/>
        <line x1="2" y1="12" x2="9" y2="12"/><line x1="15" y1="12" x2="22" y2="12"/>
      </svg>
      <span className="jalpi-preset-linker__label">Button Preset</span>
      <select
        className="jalpi-preset-linker__select"
        value={ref?.buttonId ?? ''}
        onChange={(e) => store.linkNodeToPreset(node.id, e.target.value ? { buttonId: e.target.value } : undefined)}
      >
        <option value="">— None —</option>
        {gs.buttons.map((b) => <option key={b.id} value={b.id}>{b.name}</option>)}
      </select>
      {linked && (
        <button
          className="jalpi-preset-linker__unlink"
          onClick={() => store.linkNodeToPreset(node.id, undefined)}
          title="Remove preset link"
        >
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
        </button>
      )}
    </div>
  );
}

// ── App ───────────────────────────────────────────────────────────────────────

export function App() {
  const store   = useEditorStore(jalpiEditorConfig.postId, jalpiEditorConfig.designSystem);
  const api     = useApi();
  const [postTitle, setPostTitle]         = useState('');
  const [error, setError]                 = useState<string | null>(null);
  const [toast, setToast]                 = useState<string | null>(null);
  const [inspTab, setInspTab]             = useState<InspectorTab>('style');
  const [showElements, setShowElements]   = useState(true);
  const [showRevisionPanel, setShowRevisionPanel]     = useState(false);
  const [showFinder,        setShowFinder]            = useState(false);
  const [saveTemplateNode, setSaveTemplateNode]       = useState<CBNode | null | undefined>(undefined);
  const [timelineNodeId, setTimelineNodeId]           = useState<string | null>(null);
  // Panel widths — persisted only for this session
  const [inspWidth,  setInspWidth]  = useState(300);
  const [elemWidth,  setElemWidth]  = useState(200);
  const inspectorRef   = useRef<HTMLDivElement>(null);
  const elemPanelRef   = useRef<HTMLDivElement>(null);
  const autoSaveRef          = useRef<ReturnType<typeof setTimeout> | null>(null);
  const revisionIntervalRef  = useRef<ReturnType<typeof setInterval> | null>(null);
  const toastTimerRef        = useRef<ReturnType<typeof setTimeout> | null>(null);
  // Refs so effects always call the latest versions without re-registering
  const handleSaveRef        = useRef<(silent?: boolean) => Promise<void>>(async () => {});
  const storeRef             = useRef(store);

  // Keep refs up to date every render
  storeRef.current = store;

  useKeyboard(store);

  // Ctrl+P global finder
  useEffect(() => {
    const h = () => setShowFinder(v => !v);
    window.addEventListener('cb:openFinder', h);
    return () => window.removeEventListener('cb:openFinder', h);
  }, []);

  // Open elements panel from canvas "+" button in empty containers/columns
  useEffect(() => {
    const h = () => setShowElements(true);
    window.addEventListener('cb:openElements', h);
    return () => window.removeEventListener('cb:openElements', h);
  }, []);

  // Toast messages dispatched from canvas components (e.g. copy/paste page)
  // showToast is stable (useCallback with [] deps) — safe to use [] here.
  // Using [showToast] would hit the temporal dead zone because showToast is declared later in this component.
  useEffect(() => {
    const h = (e: Event) => showToast((e as CustomEvent<string>).detail);
    window.addEventListener('cb:toast', h as EventListener);
    return () => window.removeEventListener('cb:toast', h as EventListener);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // Open SaveTemplateModal when context menu or other UI dispatch this event
  useEffect(() => {
    const handler = (e: Event) => {
      const nodeId = (e as CustomEvent<{ nodeId?: string }>).detail?.nodeId;
      if (nodeId) {
        const findNode = (nodes: CBNode[], id: string): CBNode | null => {
          for (const n of nodes) {
            if (n.id === id) return n;
            const found = findNode(n.children ?? [], id);
            if (found) return found;
          }
          return null;
        };
        setSaveTemplateNode(findNode(storeRef.current.state.pageData.sections, nodeId));
      } else {
        setSaveTemplateNode(null); // null = page template
      }
    };
    window.addEventListener('cb:openSaveTemplate', handler as EventListener);
    return () => window.removeEventListener('cb:openSaveTemplate', handler as EventListener);
  }, []);

  // Open the Timeline Editor when a context-menu action dispatches this event.
  // Stores only the node id (not a resolved node snapshot) — the panel below
  // re-resolves it from the live store on every render via findNodeById, the
  // same helper selectedNode already uses, so edits made inside the Timeline
  // (adding/moving rules) are reflected immediately rather than operating on
  // a stale copy.
  useEffect(() => {
    const handler = (e: Event) => {
      const nodeId = (e as CustomEvent<{ nodeId?: string }>).detail?.nodeId;
      if (nodeId) setTimelineNodeId(nodeId);
    };
    window.addEventListener('cb:openTimeline', handler as EventListener);
    return () => window.removeEventListener('cb:openTimeline', handler as EventListener);
  }, []);

  // Inject Design System CSS vars into document <head> for editor canvas preview.
  // Uses the exact same --jalpi-* naming Jalpi_Settings::output_design_system_css()
  // emits on the real frontend, so the canvas stays pixel-matched with production.
  // Supersedes the old --gc-*/--gs-* preview-only vars (nothing else references them).
  useEffect(() => {
    const ds = store.state.designSystem;
    const lines: string[] = [];
    for (const category of TOKEN_CATEGORIES) {
      for (const t of ds[category] as DesignToken[]) {
        if (t.value) lines.push(`  ${cssVarName(category, t.id)}: ${t.value};`);
      }
    }
    for (const p of ds.typography) {
      for (const [key, val] of Object.entries(p)) {
        if (key === 'id' || key === 'name' || !val) continue;
        lines.push(`  ${typoVarName(p.id, key)}: ${val};`);
      }
    }
    for (const b of ds.buttons) {
      for (const [key, val] of Object.entries(b)) {
        if (key === 'id' || key === 'name' || !val) continue;
        lines.push(`  ${btnVarName(b.id, key)}: ${val};`);
      }
    }
    let el = document.getElementById('jalpi-global-vars');
    if (!el) {
      el = document.createElement('style');
      el.id = 'jalpi-global-vars';
      document.head.appendChild(el);
    }
    el.textContent = `:root {\n${lines.join('\n')}\n}`;
  }, [store.state.designSystem]);

  // Initial load
  useEffect(() => {
    api.loadPage()
      .then((res) => {
        setPostTitle(res.post_title);
        storeRef.current.loadPageData(res.page_data);
      })
      .catch((err) => setError(err.message));
  }, []);

  // Reusable classes are loaded once here (not lazily inside ClassesPanel) so
  // the PropertiesPanel class picker has data ready regardless of which
  // inspector tab is open first.
  useEffect(() => {
    api.getClasses()
      .then((classes) => storeRef.current.setClasses(classes))
      .catch(() => {});
  }, []);

  // Auto-save 3s after last change — uses ref so closure is always fresh
  useEffect(() => {
    if (!store.state.isDirty) return;
    if (autoSaveRef.current) clearTimeout(autoSaveRef.current);
    autoSaveRef.current = setTimeout(() => handleSaveRef.current(true), 3000);
    return () => { if (autoSaveRef.current) clearTimeout(autoSaveRef.current); };
  }, [store.state.pageData, store.state.isDirty]);

  // Ctrl+S — registered once, always calls latest handleSave
  useEffect(() => {
    const handler = () => handleSaveRef.current(false);
    window.addEventListener('cb:save', handler);
    return () => window.removeEventListener('cb:save', handler);
  }, []);

  // Revision auto-save every 60s — passes the last history label so the revision
  // timeline can surface a meaningful description (e.g. "Add Element", "Delete", etc.)
  useEffect(() => {
    revisionIntervalRef.current = setInterval(() => {
      const s = storeRef.current.state;
      const label = s.history[s.historyIndex]?.label ?? 'Auto-save';
      if (s.pageData) api.createRevision(s.pageData, label).catch(() => {});
    }, 60_000);
    return () => { if (revisionIntervalRef.current) clearInterval(revisionIntervalRef.current); };
  }, []);

  // Auto-switch to Style tab whenever a node is selected (even the same one re-selected).
  // Uses selectionSeq so clicking an already-selected node in Outline always brings back the Style panel.
  useEffect(() => {
    if (store.state.selectedId) setInspTab('style');
  }, [store.state.selectionSeq]);

  // Warn on unsaved
  useEffect(() => {
    const handler = (e: BeforeUnloadEvent) => {
      if (storeRef.current.state.isDirty) { e.preventDefault(); e.returnValue = ''; }
    };
    window.addEventListener('beforeunload', handler);
    return () => window.removeEventListener('beforeunload', handler);
  }, []);

  const showToast = useCallback((msg: string) => {
    if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
    setToast(msg);
    toastTimerRef.current = setTimeout(() => setToast(null), 2500);
  }, []);

  const handleSave = useCallback(async (silent = false) => {
    const s = storeRef.current;
    if (s.state.isSaving) return;
    s.setSaving(true);
    try {
      await api.savePage(s.state.pageData);
      s.markSaved();
      if (!silent) showToast('Saved');
    } catch (err: unknown) {
      showToast(err instanceof Error ? err.message : 'Save failed');
    } finally {
      s.setSaving(false);
    }
  }, [api, showToast]);

  // Keep handleSaveRef in sync
  handleSaveRef.current = handleSave;

  // Design System save — deliberately independent of handleSave/page save.
  // Linking a widget to a token edits the PAGE (isDirty, saved above); editing
  // a token's own value edits the site-wide Design System (designSystemDirty,
  // saved here). Distinct toast copy so the two flows are never ambiguous.
  const handleSaveDesignSystem = useCallback(async () => {
    const s = storeRef.current;
    if (s.state.designSystemSaving) return;
    s.setDesignSystemSaving(true);
    try {
      await api.saveDesignSystem(s.state.designSystem);
      s.markDesignSystemSaved();
      showToast('Design System saved');
    } catch (err: unknown) {
      showToast(err instanceof Error ? err.message : 'Design System save failed');
    } finally {
      s.setDesignSystemSaving(false);
    }
  }, [api, showToast]);

  const handleSaveDesignSystemRef = useRef(handleSaveDesignSystem);
  handleSaveDesignSystemRef.current = handleSaveDesignSystem;

  // Auto-save 3s after last Design System change, mirroring the page autosave below.
  useEffect(() => {
    if (!store.state.designSystemDirty) return;
    const t = setTimeout(() => handleSaveDesignSystemRef.current(), 3000);
    return () => clearTimeout(t);
  }, [store.state.designSystem, store.state.designSystemDirty]);

  const handlePublish = useCallback(async () => {
    const s = storeRef.current;
    if (s.state.isPublishing) return;
    s.setPublishing(true);
    try {
      await handleSave(true);
      await api.publishPage();
      showToast('Published!');
    } catch (err: unknown) {
      showToast(err instanceof Error ? err.message : 'Publish failed');
    } finally {
      s.setPublishing(false);
    }
  }, [api, handleSave, showToast]);

  const handlePreview = useCallback(async () => {
    await handleSave(true);
    window.open(jalpiEditorConfig.previewUrl, '_blank');
  }, [handleSave]);

  // Add element from the ElementsPanel — inserts after the containing section
  const handleAddElement = useCallback((defaultNode: Partial<CBNode>) => {
    const type = defaultNode.type;
    if (!type) return;

    const s = storeRef.current;

    if (type === 'section') {
      const { selectedId, pageData } = s.state;
      // Insert new section after the one that contains the selected element
      const afterId = selectedId
        ? (pageData.sections.find((sec) => sec.id === selectedId)?.id ??
           findParentSectionId(selectedId, pageData.sections))
        : undefined;
      s.addSection(afterId);
      return;
    }

    const { selectedId, pageData } = s.state;
    if (selectedId) {
      const sel = findNodeById(selectedId, pageData.sections);
      if (sel?.type === 'section') {
        s.addElementInContainer(selectedId, defaultNode);
      } else if (sel?.type === 'container' || sel?.type === 'column') {
        s.addElement(selectedId, defaultNode);
      } else {
        // Leaf element selected: insert new section right after the containing section
        const parentSectionId = findParentSectionId(selectedId, pageData.sections);
        s.addElementInNewSection(parentSectionId, defaultNode);
      }
    } else {
      s.addElementInNewSection(undefined, defaultNode);
    }
  }, []);

  if (error) {
    return (
      <div className="jalpi-error-screen">
        <h2>Jalpi Page Builder Error</h2>
        <p>{error}</p>
        <a href={window.location.href}>Reload</a>
      </div>
    );
  }

  const { selectedId } = store.state;
  const selectedNode   = selectedId ? findNodeById(selectedId, store.state.pageData.sections) : null;
  const timelineNode   = timelineNodeId ? findNodeById(timelineNodeId, store.state.pageData.sections) : null;

  return (
    <div className="jalpi-editor">
      <TopBar
        store={store}
        postTitle={postTitle}
        showElements={showElements}
        onToggleElements={() => setShowElements((v) => !v)}
        onSave={() => handleSave(false)}
        onPublish={handlePublish}
        onPreview={handlePreview}
        onOpenRevisions={() => setShowRevisionPanel(true)}
      />

      <div className="jalpi-editor__body">
        {/* Left: Elements panel (resizable) */}
        {showElements && (
          <>
            <div
              ref={elemPanelRef}
              className="jalpi-elements-panel-wrap"
              style={{ width: elemWidth, flexShrink: 0 }}
            >
              <ElementsPanel
                onAdd={handleAddElement}
                onClose={() => setShowElements(false)}
                onOpenRevisions={() => setShowRevisionPanel(true)}
              />
            </div>
            <PanelResizer
              targetRef={elemPanelRef}
              direction="left"
              min={160}
              max={480}
              onCommit={setElemWidth}
            />
          </>
        )}

        {/* Center: Canvas */}
        <main className="jalpi-editor__canvas-area">
          <Canvas store={store} />
        </main>

        {/* Right: Inspector (resizable) */}
        <PanelResizer
          targetRef={inspectorRef}
          direction="right"
          min={220}
          max={560}
          onCommit={setInspWidth}
        />
        <aside ref={inspectorRef} className="jalpi-editor__inspector" style={{ width: inspWidth, flexShrink: 0 }}>
          {/* 3-tab nav */}
          <div className="jalpi-inspector-nav">
            <button
              className={`jalpi-inspector-nav__btn${inspTab === 'outline' ? ' is-active' : ''}`}
              onClick={() => setInspTab('outline')}
              title="Page Outline"
            >
              <OutlineIcon />
              Outline
            </button>
            <button
              className={`jalpi-inspector-nav__btn${inspTab === 'style' ? ' is-active' : ''}`}
              onClick={() => setInspTab('style')}
              title="Style Inspector"
            >
              <StyleIcon />
              Style
            </button>
            <button
              className={`jalpi-inspector-nav__btn${inspTab === 'page' ? ' is-active' : ''}`}
              onClick={() => setInspTab('page')}
              title="Page Settings"
            >
              <PageIcon />
              Page
            </button>
            <button
              className={`jalpi-inspector-nav__btn${inspTab === 'global' ? ' is-active' : ''}`}
              onClick={() => setInspTab('global')}
              title="Global Styles"
            >
              <GlobalIcon />
              Global
            </button>
            <button
              className={`jalpi-inspector-nav__btn${inspTab === 'classes' ? ' is-active' : ''}`}
              onClick={() => setInspTab('classes')}
              title="Reusable Classes"
            >
              <ClassesIcon />
              Classes
            </button>
          </div>

          {/* Tab body */}
          <div className="jalpi-inspector-body">
            {inspTab === 'outline' && <LayersPanel store={store} />}
            {inspTab === 'style'   && <PropertiesPanel store={store} />}
            {inspTab === 'page'    && <PagePanel postTitle={postTitle} />}
            {inspTab === 'global'  && <GlobalStylesPanel store={store} onSave={handleSaveDesignSystem} />}
            {inspTab === 'classes' && <ClassesPanel store={store} api={api} onToast={showToast} />}
          </div>

          {/* Quick Actions — visible when a node is selected */}
          {selectedNode && (
            <div className="jalpi-quick-actions">
              <div className="jalpi-quick-actions__title">QUICK ACTIONS</div>
              <div className="jalpi-quick-actions__btns">
                <button
                  className="jalpi-quick-actions__btn"
                  onClick={() => store.duplicateNode(selectedId!)}
                  title="Duplicate (Ctrl+D)"
                >
                  <DuplicateIcon />
                  <span>Duplicate</span>
                </button>
                <button
                  className="jalpi-quick-actions__btn"
                  onClick={() => store.copyNode(selectedId!)}
                  title="Copy (Ctrl+C)"
                >
                  <CopyIcon />
                  <span>Copy</span>
                </button>
                <button
                  className="jalpi-quick-actions__btn jalpi-quick-actions__btn--danger"
                  onClick={() => store.deleteNode(selectedId!)}
                  title="Delete (Del)"
                >
                  <DeleteIcon />
                  <span>Delete</span>
                </button>
              </div>
            </div>
          )}
        </aside>
      </div>

      {/* Modals */}
      {showRevisionPanel && (
        <RevisionPanel
          store={store}
          api={api}
          onClose={() => setShowRevisionPanel(false)}
          onToast={showToast}
        />
      )}

      {saveTemplateNode !== undefined && (
        <SaveTemplateModal
          node={saveTemplateNode}
          store={store}
          onClose={() => setSaveTemplateNode(undefined)}
          onSaved={() => showToast('Template saved')}
        />
      )}

      {timelineNode && (
        <TimelinePanel
          node={timelineNode}
          store={store}
          onClose={() => setTimelineNodeId(null)}
        />
      )}

      {showFinder && (
        <GlobalFinder
          store={store}
          api={api}
          onClose={() => setShowFinder(false)}
          onOpenRevisions={() => setShowRevisionPanel(true)}
        />
      )}

      {toast && <div className="jalpi-toast" role="status">{toast}</div>}
    </div>
  );
}
