import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
import type {
  Theme, RibbonTab, ViewMode, RightPanel, DrawerTab,
  CursorPos, CommandHistoryEntry, KIMessage, KISuggestion,
} from './types/ui.types';
import type { CADElement, CADLayer, BlockDefinition, ImageElement } from './types/cad.types';
import { BlockService } from './services/blockService';
import { SeatingService } from './services/seatingService';
import type { ToolState } from './interaction';
import { GroupManager, type ElementGroup } from './tools/modification/GroupTool';
import Topbar from './components/Topbar';
import RibbonBar from './components/RibbonBar';
import LeftSidebar from './components/LeftSidebar';
import CanvasArea from './components/CanvasArea';
import RightSidebar from './components/RightSidebar';
import CommandLine from './components/CommandLine';
import StatusBar from './components/StatusBar';
import MobileDrawers from './components/MobileDrawers';
import BackgroundImport from './components/BackgroundImport';
import HistoryPanel from './components/HistoryPanel';
import SettingsModal from './components/SettingsModal';
import InlineTextEditor from './components/InlineTextEditor';
import { BackgroundService, type BackgroundConfig } from './services/backgroundService';
import { HistoryManager, type CADStateSnapshot, type HistoryEntry } from './history';
import { getCommandRegistry } from './services/commandRegistry';
import { importFile, type ImportResult } from './services/importService';
import { exportProject, downloadBlob, type ExportFormat } from './services/exportService';
import type { ProjectData } from './types/cad.types';
import { loadProjectDataTyped, createElementTyped, updateElement, deleteElement as apiDeleteElement, createLayerTyped, updateLayer, deleteLayer as apiDeleteLayer, createBlockTyped, updateBlock, deleteBlock as apiDeleteBlock, aiChat } from './services/api';
import { useYjsBinding } from './crdt';
import { registerBuiltinPlugins, pluginRegistry } from './plugins';
import type { PluginContext } from './plugins';
import './styles.css';
import './styles/auth.css';
import { useAuth } from './contexts/AuthContext';
import { Login } from './pages/Login';
import { Register } from './pages/Register';
import { Dashboard } from './pages/Dashboard';

// ─── Initial State ──────────────────────────────────────────
// Data is loaded from the backend on mount — start with empty state.
const initialLayers: CADLayer[] = [];
const initialBlocks: BlockDefinition[] = [];

const initialCommandHistory: CommandHistoryEntry[] = [
  { prefix: '·', text: 'Bereit', type: 'info' },
];

const initialKIMessages: KIMessage[] = [
  { id: 'ki-1', role: 'assistant', content: 'Hallo! Ich bin der KI Copilot. Wie kann ich helfen?' },
];

const initialKISuggestions: KISuggestion[] = [
  { id: 'sug-1', label: 'Bestuhlung automatisch generieren' },
  { id: 'sug-2', label: 'Maße analysieren' },
  { id: 'sug-3', label: 'Flächen berechnen' },
];

// ─── CAD Editor Component ───────────────────────────────
interface CADEditorProps {
  projectId: string;
  token: string;
  onNavigateBack: () => void;
}

const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack }) => {
  // Theme
  const [theme, setTheme] = useState<Theme>('dark');

  // Auth user for collaboration
  const { user } = useAuth();

  // Yjs collaboration binding
  const collab = useYjsBinding({
    docName: `project-${projectId}`,
    userId: user?.id || 'anonymous',
    userName: user?.name || 'Gast',
    userColor: '#3498db',
    enabled: true,
    token: token || undefined,
  });

  // Project
  const [projectName, setProjectName] = useState('Unbenannt');
  const [savedStatus, setSavedStatus] = useState('Lädt…');
  const [drawingId, setDrawingId] = useState<string | null>(null);

  // Ribbon
  const [activeRibbonTab, setActiveRibbonTab] = useState<RibbonTab>('start');

  // Tools
  const [activeTool, setActiveTool] = useState('select');

  // Canvas / View
  const [viewMode, setViewMode] = useState<ViewMode>('2d');
  const [cursorPos, setCursorPos] = useState<CursorPos>({ x: 0, y: 0 });
  const [gridEnabled, setGridEnabled] = useState(true);
  const [orthoEnabled, setOrthoEnabled] = useState(false);
  const [snapEnabled, setSnapEnabled] = useState(true);
  const [polarEnabled, setPolarEnabled] = useState(false);

  // Right sidebar
  const [activeRightPanel, setActiveRightPanel] = useState<RightPanel>('tool');
  const [selectedElement, setSelectedElement] = useState<CADElement | null>(null);
  const [selectedElementIds, setSelectedElementIds] = useState<string[]>([]);
  const [layers, setLayers] = useState<CADLayer[]>(initialLayers);
  const [activeLayerId, setActiveLayerId] = useState<string>('layer-0');
  const [blocks, setBlocks] = useState<BlockDefinition[]>(initialBlocks);
  const [blockCategory, setBlockCategory] = useState('Alle');
  const [selectedTemplate, setSelectedTemplate] = useState<string | null>(null);

  // Command line
  const [commandHistory, setCommandHistory] = useState<CommandHistoryEntry[]>(initialCommandHistory);

  // KI Copilot
  const [kiMessages, setKIMessages] = useState<KIMessage[]>(initialKIMessages);
  const [kiSuggestions] = useState<KISuggestion[]>(initialKISuggestions);
  const [kiLoading, setKiLoading] = useState(false);

  // Plugin system
  const createPluginContext = useCallback((): PluginContext => ({
    addElement: (el) => setElements((prev) => [...prev, el]),
    removeElement: (id) => setElements((prev) => prev.filter((e) => e.id !== id)),
    updateElement: (id, props) => setElements((prev) => prev.map((e) => (e.id === id ? { ...e, ...props } : e))),
    getElements: () => elementsRef.current,
    getLayers: () => layersRef.current,
    getActiveLayerId: () => activeLayerIdRef.current,
    showToast: (msg) => setCommandHistory((prev) => [...prev, { prefix: '·', text: msg, type: 'info' }]),
    log: (msg) => setCommandHistory((prev) => [...prev, { prefix: '·', text: `[Plugin] ${msg}`, type: 'info' }]),
  }), []);

  useEffect(() => {
    pluginRegistry.setContext(createPluginContext());
    registerBuiltinPlugins();
    pluginRegistry.initDefaults();
  }, [createPluginContext]);

  // Status bar – only count self as online when actually connected
  const onlineCount = collab.status === 'connected' ? collab.cursors.length : 0;

  // Mobile drawers
  const [mobileLeftOpen, setMobileLeftOpen] = useState(false);
  const [mobileRightOpen, setMobileRightOpen] = useState(false);
  // Desktop sidebar collapse
  const [leftSidebarCollapsed, setLeftSidebarCollapsed] = useState(false);
  const [rightSidebarCollapsed, setRightSidebarCollapsed] = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [activeDrawerTab, setActiveDrawerTab] = useState<DrawerTab>('tool');

  // Background
  const [bgImportOpen, setBgImportOpen] = useState(false);
  const [bgConfig, setBgConfig] = useState<BackgroundConfig | null>(null);
  const bgServiceRef = React.useRef<BackgroundService>(new BackgroundService());

  // Cleanup BackgroundService on unmount
  useEffect(() => {
    return () => {
      bgServiceRef.current.dispose();
    };
  }, []);

  // History panel
  const [historyPanelOpen, setHistoryPanelOpen] = useState(false);

  // Export format selector (cross-browser replacement for nwsave)
  const [exportFormatOpen, setExportFormatOpen] = useState(false);

  // Zoom command state — triggers zoom actions in CanvasArea via useEffect
  const [zoomCommand, setZoomCommand] = useState<'in' | 'out' | 'fit' | '100' | null>(null);

  // Elements + undo/redo (HistoryManager)
  const [elements, setElements] = useState<CADElement[]>([]);
  const historyManagerRef = React.useRef<HistoryManager>(new HistoryManager());
  const [historyEntries, setHistoryEntries] = useState<HistoryEntry[]>([]);
  const [toolState, setToolState] = useState<ToolState | null>(null);

  const seatingServiceRef = useRef<SeatingService>(new SeatingService());
  const seatCount = useMemo(() => {
    return seatingServiceRef.current.countSeats(elements).total;
  }, [elements]);

  // Groups
  const groupManagerRef = React.useRef(new GroupManager());
  const [groups, setGroups] = useState<ElementGroup[]>([]);

  // Issue #8: Refs for PluginContext to avoid stale closures.
  // These refs are updated on every render so PluginContext getters always return current state.
  const elementsRef = useRef(elements);
  elementsRef.current = elements;
  const layersRef = useRef(layers);
  layersRef.current = layers;
  const blocksRef = useRef(blocks);
  blocksRef.current = blocks;
  const groupsRef = useRef(groups);
  groupsRef.current = groups;
  const bgConfigRef = useRef(bgConfig);
  bgConfigRef.current = bgConfig;
  const activeLayerIdRef = useRef(activeLayerId);
  activeLayerIdRef.current = activeLayerId;
  const selectedElementIdsRef = useRef(selectedElementIds);
  selectedElementIdsRef.current = selectedElementIds;

  // Clipboard (for copy/paste)
  const clipboardRef = React.useRef<CADElement[] | null>(null);

  // ─── Handlers ───────────────────────────────────────────
  const handleThemeToggle = useCallback(() => {
    setTheme((prev) => (prev === 'dark' ? 'light' : 'dark'));
  }, []);

  const syncHistory = useCallback(() => {
    setHistoryEntries(historyManagerRef.current.getHistory());
  }, []);

  const restoreSnapshot = useCallback((snap: CADStateSnapshot) => {
    setElements(snap.elements);
    setLayers(snap.layers);
    setBlocks(snap.blocks);
    setGroups(snap.groups);
    setBgConfig(snap.bgConfig);
  }, []);

  const handleUndo = useCallback(() => {
    const snap = historyManagerRef.current.undo();
    if (snap) {
      restoreSnapshot(snap);
      syncHistory();
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Rückgängig: letzte Aktion', type: 'info' }]);
    }
  }, [restoreSnapshot, syncHistory]);

  const handleRedo = useCallback(() => {
    const snap = historyManagerRef.current.redo();
    if (snap) {
      restoreSnapshot(snap);
      syncHistory();
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Wiederherstellen: letzte Aktion', type: 'info' }]);
    }
  }, [restoreSnapshot, syncHistory]);

  const pushHistorySnapshot = useCallback((label: string) => {
    historyManagerRef.current.pushSnapshot({
      elements,
      layers,
      blocks,
      groups,
      bgConfig,
    }, label);
    syncHistory();
  }, [elements, layers, blocks, groups, bgConfig, syncHistory]);

  // Initialize HistoryManager with initial state on mount
  const historyInitRef = React.useRef(false);
  React.useEffect(() => {
    if (historyInitRef.current) return;
    historyInitRef.current = true;
    historyManagerRef.current.initialize({
      elements: [],
      layers: initialLayers,
      blocks: initialBlocks,
      groups: [],
      bgConfig: null,
    });
    syncHistory();
  }, [syncHistory]);

  // Load project data from backend on mount
  const [dataLoaded, setDataLoaded] = useState(false);
  React.useEffect(() => {
    if (!projectId || !token) return;
    let cancelled = false;
    (async () => {
      try {
        setSavedStatus('Lädt…');
        const data = await loadProjectDataTyped(token, projectId);
        if (cancelled) return;
        setProjectName(data.project.name);
        setDrawingId(data.drawing?.id || null);
        if (data.elements.length > 0) setElements(data.elements);
        if (data.layers.length > 0) setLayers(data.layers);
        if (data.blocks.length > 0) setBlocks(data.blocks);
        // Ensure activeLayerId matches a real layer
        if (data.layers.length > 0) {
          const hasActiveLayer = data.layers.some(l => l.id === 'layer-0');
          if (!hasActiveLayer) {
            setActiveLayerId(data.layers[0].id);
          }
        }
        // Create default layer if backend has none
        if (data.layers.length === 0 && data.drawing) {
          const defaultLayer: CADLayer = {
            id: 'layer-0',
            name: 'Layer 0',
            visible: true,
            locked: false,
            color: '#ffffff',
            lineType: 'solid',
            transparency: 0,
            sortOrder: 0,
            parentId: null,
          };
          createLayerTyped(token, data.drawing.id, defaultLayer).catch(() => {});
          setLayers([defaultLayer]);
          setActiveLayerId('layer-0');
        }
        setSavedStatus('gespeichert');
        setDataLoaded(true);
        // Push initial data to Yjs CRDT after load
        // Issue #7: Only push to CRDT if backend data actually has content.
        // Pushing empty arrays would wipe existing CRDT data from other connected clients.
        const bgConfigs = bgConfig ? [bgConfig] : [];
        const hasContent = data.elements.length > 0 || data.layers.length > 0 || data.blocks.length > 0 || groups.length > 0 || bgConfigs.length > 0;
        if (collab.status === 'connected' && hasContent) {
          collab.loadFromState({
            elements: data.elements,
            layers: data.layers,
            blocks: data.blocks,
            groups,
            bgConfigs,
          });
        }
      } catch (err) {
        console.error('Failed to load project:', err);
        setSavedStatus('Fehler beim Laden');
      }
    })();
    return () => { cancelled = true; };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [projectId, token]);

  // Sync remote → local: when Yjs data changes from other users, update local state
  React.useEffect(() => {
    if (collab.status !== 'connected') return;
    // Skip if collab data is empty (not yet loaded)
    if (collab.elements.length === 0 && collab.layers.length === 0 && collab.blocks.length === 0) return;

    // Only update local state if remote data differs AND local is not ahead of remote
    // (prevents race condition where local element is overwritten by stale Yjs sync)
    // Merge: update local elements with remote version when properties differ, add remote-only elements
    const localIds = new Set(elements.map(e => e.id));
    const remoteIds = new Set(collab.elements.map(e => e.id));
    const remoteMap = new Map(collab.elements.map(e => [e.id, e]));
    let changed = false;
    const merged = elements.map(el => {
      const remote = remoteMap.get(el.id);
      if (remote && JSON.stringify(remote) !== JSON.stringify(el)) {
        changed = true;
        return remote;
      }
      return el;
    });
    // Add remote-only elements
    collab.elements.forEach(el => {
      if (!localIds.has(el.id)) {
        changed = true;
        merged.push(el);
      }
    });
    if (changed) {
      setElements(merged);
    }

    // Merge layers: local-first, update changed from remote, append remote-only
    if (collab.layers.length > 0) {
      const localLayerMap = new Map(layers.map(l => [l.id, l]));
      const remoteLayerMap = new Map(collab.layers.map(l => [l.id, l]));
      const mergedLayers = layers.map(ll => {
        const remote = remoteLayerMap.get(ll.id);
        return remote ? (JSON.stringify(remote) !== JSON.stringify(ll) ? remote : ll) : ll;
      });
      collab.layers.forEach(rl => {
        if (!localLayerMap.has(rl.id)) mergedLayers.push(rl);
      });
      if (mergedLayers.length !== layers.length || mergedLayers.some((ml, i) => ml !== layers[i])) {
        setLayers(mergedLayers);
      }
      // Ensure activeLayerId matches a real layer after collab merge
      const hasActiveInMerged = mergedLayers.some(l => l.id === activeLayerId);
      if (!hasActiveInMerged && mergedLayers.length > 0) {
        setActiveLayerId(mergedLayers[0].id);
      }
    }

    // Merge blocks: local-first, update changed from remote, append remote-only
    if (collab.blocks.length > 0) {
      const localBlockMap = new Map(blocks.map(b => [b.id, b]));
      const remoteBlockMap = new Map(collab.blocks.map(b => [b.id, b]));
      const mergedBlocks = blocks.map(lb => {
        const remote = remoteBlockMap.get(lb.id);
        return remote ? (JSON.stringify(remote) !== JSON.stringify(lb) ? remote : lb) : lb;
      });
      collab.blocks.forEach(rb => {
        if (!localBlockMap.has(rb.id)) mergedBlocks.push(rb);
      });
      if (mergedBlocks.length !== blocks.length || mergedBlocks.some((mb, i) => mb !== blocks[i])) {
        setBlocks(mergedBlocks);
      }
    }

    // Sync groups from remote → local
    if (collab.groups.length > 0) {
      const sameGroups = collab.groups.length === groups.length &&
        collab.groups.every((g, i) => g.id === groups[i]?.id);
      if (!sameGroups) setGroups(collab.groups);
    }

    // Sync bgConfig from remote → local
    if (collab.bgConfigs.length > 0) {
      const remoteBg = collab.bgConfigs[0];
      if (!bgConfig || remoteBg.src !== bgConfig.src || remoteBg.scale !== bgConfig.scale ||
          remoteBg.offsetX !== bgConfig.offsetX || remoteBg.offsetY !== bgConfig.offsetY ||
          remoteBg.visible !== bgConfig.visible || remoteBg.opacity !== bgConfig.opacity) {
        setBgConfig(remoteBg);
      }
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [collab.elements, collab.layers, collab.blocks, collab.groups, collab.bgConfigs, collab.status]);

  // Sync local → remote: push local groups to Yjs when groups change
  React.useEffect(() => {
    if (collab.status !== 'connected') return;
    for (const group of groups) {
      collab.setGroup(group);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [groups, collab.status]);

  // Sync local → remote: push local bgConfig to Yjs when bgConfig changes
  React.useEffect(() => {
    if (collab.status !== 'connected') return;
    if (bgConfig) {
      collab.setBgConfig(bgConfig.name || 'default', bgConfig);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [bgConfig, collab.status]);

  // Sync local → remote: push local layers to Yjs when layers change
  React.useEffect(() => {
    if (collab.status !== 'connected') return;
    for (const layer of layers) collab.setLayer(layer);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [layers, collab.status]);

  // Sync local → remote: push local blocks to Yjs when blocks change
  React.useEffect(() => {
    if (collab.status !== 'connected') return;
    for (const block of blocks) collab.setBlock(block);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [blocks, collab.status]);

  const handleElementCreated = useCallback((el: CADElement) => {
    const elWithLayer = el.layerId ? el : { ...el, layerId: activeLayerId };
    setElements((prev) => {
      const newElements = [...prev, elWithLayer];
      historyManagerRef.current.pushSnapshot({
        elements: newElements, layers, blocks, groups, bgConfig,
      }, 'Element erstellt');
      return newElements;
    });
    syncHistory();
    // Push to Yjs CRDT for real-time sync
    collab.setElement(elWithLayer);
    // Save to backend
    if (drawingId && token) {
      setSavedStatus('Speichert…');
      createElementTyped(token, drawingId, elWithLayer).then(() => {
        setSavedStatus('gespeichert');
      }).catch((err) => {
        console.error('Failed to save element:', err);
        setSavedStatus('Fehler beim Speichern');
      });
    }
  }, [layers, blocks, groups, bgConfig, syncHistory, drawingId, token, collab, activeLayerId]);

  const handleToggleElementVisible = useCallback((id: string) => {
    const target = elements.find(e => e.id === id);
    if (!target) return;
    const updatedEl: CADElement = { ...target, properties: { ...target.properties, visible: target.properties?.visible === false ? true : false } };
    setElements((prev) => prev.map((e) => e.id === id ? updatedEl : e));
    // Call API outside the state updater
    if (token) {
      setSavedStatus('Speichert…');
      updateElement(token, updatedEl.id, updatedEl).then(() => setSavedStatus('gespeichert')).catch((err) => {
        console.error('Failed to toggle element visibility:', err);
        setSavedStatus('Fehler beim Speichern');
      });
    }
  }, [elements, token]);

  const handleElementsDeleted = useCallback((ids: string[]) => {
    setElements((prev) => {
      const newElements = prev.filter((e) => !ids.includes(e.id));
      historyManagerRef.current.pushSnapshot({
        elements: newElements, layers, blocks, groups, bgConfig,
      }, `${ids.length} Element(e) gelöscht`);
      return newElements;
    });
    syncHistory();
    // Push deletions to Yjs CRDT for real-time sync
    ids.forEach(id => collab.deleteElement(id));
    // Delete from backend
    if (token) {
      setSavedStatus('Speichert…');
      Promise.all(ids.map(id => apiDeleteElement(token, id))).then(() => {
        setSavedStatus('gespeichert');
      }).catch((err) => {
        console.error('Failed to delete elements:', err);
        setSavedStatus('Fehler beim Speichern');
      });
    }
  }, [layers, blocks, groups, bgConfig, syncHistory, token, collab]);

  const handleElementsModified = useCallback((modified: CADElement[]) => {
    setElements((prev) => {
      const newElements = prev.map((e) => {
        const found = modified.find((m) => m.id === e.id);
        return found ?? e;
      });
      historyManagerRef.current.pushSnapshot({
        elements: newElements, layers, blocks, groups, bgConfig,
      }, `${modified.length} Element(e) geändert`);
      return newElements;
    });
    syncHistory();
    // Push modifications to Yjs CRDT for real-time sync
    modified.forEach(el => collab.setElement(el));
    // Update in backend
    if (token) {
      setSavedStatus('Speichert…');
      Promise.all(modified.map(el => updateElement(token, el.id, el))).then(() => {
        setSavedStatus('gespeichert');
      }).catch((err) => {
        console.error('Failed to update elements:', err);
        setSavedStatus('Fehler beim Speichern');
      });
    }
  }, [layers, blocks, groups, bgConfig, syncHistory, token, collab]);

  const lastCursorUpdateRef = useRef(0);
  const handleCursorMoved = useCallback((x: number, y: number) => {
    const now = performance.now();
    if (now - lastCursorUpdateRef.current < 33) return; // throttle to ~30fps
    lastCursorUpdateRef.current = now;
    setCursorPos({ x, y });
    collab.setCursor(x, y);
  }, [collab]);

  const handleToolStateChanged = useCallback((state: ToolState) => {
    setToolState(state);
  }, []);

  // Inline text editor state
  const [editingTextElement, setEditingTextElement] = useState<CADElement | null>(null);

  const handleTextEdit = useCallback((el: CADElement) => {
    setEditingTextElement(el);
  }, []);

  const handleTextEditorSubmit = useCallback((text: string) => {
    if (editingTextElement && text.trim() !== '') {
      const updatedEl = { ...editingTextElement, properties: { ...editingTextElement.properties, text } };
      setElements((prev) => prev.map((e) =>
        e.id === editingTextElement.id ? updatedEl : e
      ));
      // Push to Yjs CRDT for real-time sync
      collab.setElement(updatedEl);
      // Update in backend
      if (token) {
        setSavedStatus('Speichert…');
        updateElement(token, updatedEl.id, updatedEl).then(() => {
          setSavedStatus('gespeichert');
        }).catch((err) => {
          console.error('Failed to save text edit:', err);
          setSavedStatus('Fehler beim Speichern');
        });
      }
    }
    setEditingTextElement(null);
  }, [editingTextElement, collab, token]);

  const handleTextEditorCancel = useCallback(() => {
    setEditingTextElement(null);
  }, []);

  const handleCommandTrigger = useCallback((msg: string) => {
    setCommandHistory((prev) => [...prev, { prefix: '·', text: msg, type: 'info' }]);
  }, []);

  // Block management handlers
  const handleRenameBlock = useCallback((id: string, name: string) => {
    setBlocks((prev) => prev.map((b) => b.id === id ? { ...b, name } : b));
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block umbenannt: ${name}`, type: 'info' }]);
    // Persist rename to backend
    if (token) {
      updateBlock(token, id, { name }).catch((err) => {
        console.error('Failed to save block rename:', err);
      });
    }
  }, [token]);

  const handleDuplicateBlock = useCallback((id: string) => {
    setBlocks((prev) => {
      const block = prev.find((b) => b.id === id);
      if (!block) return prev;
      const newId = `blk-${Date.now()}`;
      const copy: BlockDefinition = {
        ...block,
        id: newId,
        name: `${block.name} (Kopie)`,
        elements: block.elements.map((el) => ({ ...el, id: `${el.id}_copy_${Date.now()}` })),
      };
      // Persist duplicate to backend
      if (drawingId && token) {
        createBlockTyped(token, drawingId, copy).catch((err) => {
          console.error('Failed to save duplicated block:', err);
        });
      }
      return [...prev, copy];
    });
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Block dupliziert', type: 'info' }]);
  }, [drawingId, token]);

  const handleDeleteBlock = useCallback((id: string) => {
    setBlocks((prev) => prev.filter((b) => b.id !== id));
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Block gelöscht', type: 'info' }]);
    // Delete from backend
    if (token) {
      apiDeleteBlock(token, id).catch((err) => {
        console.error('Failed to delete block:', err);
      });
    }
  }, [token]);

  const handleSvgImport = useCallback((svg: string, name: string, category: string) => {
    const svc = new BlockService();
    const block = svc.importSVG(svg, name, category);
    setBlocks((prev) => [...prev, block]);
    // Push to Yjs CRDT for real-time sync
    collab.setBlock(block);
    // Persist to backend
    if (drawingId && token) {
      setSavedStatus('Speichert…');
      createBlockTyped(token, drawingId, block)
        .then(() => setSavedStatus('gespeichert'))
        .catch((err) => {
          console.error('Failed to save imported SVG block:', err);
          setSavedStatus('Fehler beim Speichern');
        });
    }
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `SVG importiert: ${name}`, type: 'info' }]);
  }, [collab, drawingId, token]);

  const handleSaveGroupAsBlock = useCallback((name: string) => {
    const selectedIds = selectedElementIdsRef.current;
    const selectedEls = selectedIds.length > 0
      ? elementsRef.current.filter(e => selectedIds.includes(e.id))
      : (selectedElement ? [selectedElement] : []);
    if (selectedEls.length === 0) {
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Keine Elemente ausgewählt', type: 'info' }]);
      return;
    }
    setBlocks((prev) => {
      const newId = `blk_grp_${Date.now()}`;
      const block: BlockDefinition = {
        id: newId,
        name,
        description: 'Aus Auswahl erstellt',
        category: 'Custom',
        elements: selectedEls.map(el => ({ ...el, id: `el_${Date.now()}_${Math.random().toString(36).slice(2, 7)}` })),
      };
      // Push to Yjs CRDT for real-time sync
      collab.setBlock(block);
      // Persist to backend
      if (drawingId && token) {
        createBlockTyped(token, drawingId, block).catch((err) => {
          console.error('Failed to save group block:', err);
        });
      }
      return [...prev, block];
    });
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block erstellt: ${name} (${selectedEls.length} Elemente)`, type: 'info' }]);
  }, [selectedElement, collab, drawingId, token]);

  const handleBlockCategoryChange = useCallback((cat: string) => {
    setBlockCategory(cat);
  }, []);

  const handleTemplateSelect = useCallback((templateName: string | null) => {
    setSelectedTemplate(templateName);
    if (templateName) {
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Vorlage gewählt: ${templateName} – Klicken zum Platzieren`, type: 'info' }]);
    }
  }, []);

  const handleBlockSearch = useCallback((query: string) => {
    // BlockLibrary handles search filtering locally — this callback is for
    // any App-level side effects of block search (e.g. logging, analytics).
    if (query.trim()) {
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block-Suche: ${query}`, type: 'info' }]);
    }
  }, []);

  const handleDragBlock = useCallback((blockId: string) => {
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block gezogen: ${blockId}`, type: 'info' }]);
  }, []);

  const handleBlockDrop = useCallback((blockId: string, x: number, y: number) => {
    const svc = new BlockService();
    blocks.forEach(b => svc.addBlock(b));
    const instance = svc.createInstance(blockId, x, y, activeLayerId);
    if (instance) {
      setElements((prev) => [...prev, instance]);
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Block platziert: ${blockId} bei (${x.toFixed(2)}, ${y.toFixed(2)})`, type: 'info' }]);
      // Push to Yjs CRDT for real-time sync
      if (collab.status === 'connected') {
        collab.setElement(instance);
      }
      // Save to backend
      if (drawingId && token) {
        setSavedStatus('Speichert…');
        createElementTyped(token, drawingId, instance).then(() => {
          setSavedStatus('gespeichert');
        }).catch((err) => {
          console.error('Failed to save block instance:', err);
          setSavedStatus('Fehler beim Speichern');
        });
      }
    }
  }, [blocks, activeLayerId, collab, drawingId, token]);

  const handleSelectionChange = useCallback((selectedIds: string[]) => {
    setSelectedElementIds(selectedIds);
    if (selectedIds.length === 1) {
      const el = elements.find(e => e.id === selectedIds[0]);
      setSelectedElement(el ?? null);
    } else if (selectedIds.length === 0) {
      setSelectedElement(null);
    } else {
      // Multiple elements selected — keep the first one as a representative for the properties panel
      const el = elements.find(e => e.id === selectedIds[0]);
      setSelectedElement(el ?? null);
    }
  }, [elements]);

  const handleImport = useCallback(async (file: File) => {
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Importiere ${file.name}...`, type: 'info' }]);
    const result = await importFile(file);
    if (result.success) {
      setElements((prev) => [...prev, ...result.elements]);
      // Push imported elements to Yjs and backend
      for (const el of result.elements) {
        collab.setElement(el);
      }
      if (drawingId && token) {
        setSavedStatus('Speichert…');
        Promise.all(result.elements.map(el => createElementTyped(token, drawingId, el)))
          .then(() => setSavedStatus('gespeichert'))
          .catch((err) => {
            console.error('Failed to save imported elements:', err);
            setSavedStatus('Fehler beim Speichern');
          });
      }
      if (result.layers && result.layers.length > 0) {
        const existingIds = new Set(layers.map(l => l.id));
        const newLayers = result.layers.filter(l => !existingIds.has(l.id));
        setLayers((prev) => [...prev, ...newLayers]);
        // Push imported layers to Yjs and backend
        for (const layer of newLayers) {
          collab.setLayer(layer);
        }
        if (drawingId && token) {
          Promise.all(newLayers.map(l => createLayerTyped(token, drawingId, l)))
            .catch((err) => console.error('Failed to save imported layers:', err));
        }
      }
      if (result.blocks && result.blocks.length > 0) {
        setBlocks((prev) => [...prev, ...result.blocks!]);
        // Push imported blocks to Yjs and backend
        for (const block of result.blocks) {
          collab.setBlock(block);
        }
        if (drawingId && token) {
          Promise.all(result.blocks.map(b => createBlockTyped(token, drawingId, b)))
            .catch((err) => console.error('Failed to save imported blocks:', err));
        }
      }
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Importiert: ${result.elements.length} Elemente aus ${file.name}`, type: 'info' }]);
      if (result.warnings.length > 0) {
        result.warnings.forEach(w => setCommandHistory((prev) => [...prev, { prefix: '·', text: w, type: 'info' }]));
      }
    } else {
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Import fehlgeschlagen: ${result.error || 'Unbekannter Fehler'}`, type: 'info' }]);
    }
  }, [drawingId, token, collab, layers]);

  const handleExport = useCallback(async (format: ExportFormat) => {
    const projectData: ProjectData = {
      version: '1.0',
      name: projectName,
      layers,
      elements,
      blocks,
    };
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Exportiere als ${format.toUpperCase()}...`, type: 'info' }]);
    const result = await exportProject(projectData, { format });
    if (result.success && result.blob) {
      downloadBlob(result.blob, result.filename);
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Exportiert: ${result.filename}`, type: 'info' }]);
    } else {
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Export fehlgeschlagen: ${result.error || 'Unbekannter Fehler'}`, type: 'info' }]);
    }
  }, [layers, elements, blocks]);

  const handleRibbonAction = useCallback((action: string) => {
    setCommandHistory((prev) => [...prev, { prefix: '›', text: action, type: 'command' }]);

    // ─── File actions ───
    if (action === 'new') {
      onNavigateBack();
      return;
    }
    if (action === 'open') {
      onNavigateBack();
      return;
    }
    if (action === 'save') {
      setSavedStatus('Gespeichert');
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Projekt gespeichert', type: 'info' }]);
      // Save elements to backend if drawingId exists
      if (drawingId && token) {
        elements.forEach((el) => {
          updateElement(token, el.id, el).catch(() => {});
        });
      }
      return;
    }
    if (action === 'import') {
      const input = document.createElement('input');
      input.type = 'file';
      input.accept = '.dxf,.svg,.json';
      input.onchange = () => {
        if (input.files && input.files[0]) {
          handleImport(input.files[0]);
        }
      };
      input.click();
      return;
    }
    if (action === 'export') {
      setExportFormatOpen(true);
      return;
    }

    // ─── Edit actions ───
    if (action === 'undo') { handleUndo(); return; }
    if (action === 'redo') { handleRedo(); return; }
    if (action === 'copy') {
      const ids = selectedElementIdsRef.current;
      const selected = ids.length > 0 ? elements.filter(e => ids.includes(e.id)) : [];
      const clip = selected.length > 0 ? selected : elements.slice(0, 1);
      clipboardRef.current = clip;
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `${clip.length} Element(e) kopiert`, type: 'info' }]);
      return;
    }
    if (action === 'paste') {
      if (clipboardRef.current && clipboardRef.current.length > 0) {
        const offset = 20;
        const pasted = clipboardRef.current.map((el, i) => ({
          ...el,
          id: `el-${Date.now()}-${i}`,
          x: (el.x ?? 0) + offset,
          y: (el.y ?? 0) + offset,
        }));
        setElements((prev) => [...prev, ...pasted]);
        setCommandHistory((prev) => [...prev, { prefix: '·', text: `${pasted.length} Element(e) eingefügt`, type: 'info' }]);
        // Push pasted elements to Yjs CRDT and backend
        for (const el of pasted) {
          collab.setElement(el);
        }
        if (drawingId && token) {
          setSavedStatus('Speichert…');
          Promise.all(pasted.map(el => createElementTyped(token, drawingId, el))).then(() => {
            setSavedStatus('gespeichert');
          }).catch((err) => {
            console.error('Failed to save pasted elements:', err);
            setSavedStatus('Fehler beim Speichern');
          });
        }
      } else {
        setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zwischenablage leer', type: 'info' }]);
      }
      return;
    }

    // ─── Insert actions (set active tool) ───
    if (action === 'insert-line') { setActiveTool('line'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Linie-Werkzeug aktiv', type: 'info' }]); return; }
    if (action === 'insert-rect') { setActiveTool('rect'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Rechteck-Werkzeug aktiv', type: 'info' }]); return; }
    if (action === 'insert-circle') { setActiveTool('circle'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Kreis-Werkzeug aktiv', type: 'info' }]); return; }
    if (action === 'insert-text') { setActiveTool('text'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Text-Werkzeug aktiv', type: 'info' }]); return; }
    if (action === 'insert-freehand') { setActiveTool('polyline'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Freihand-Werkzeug aktiv', type: 'info' }]); return; }
    if (action === 'insert-image') {
      const input = document.createElement('input');
      input.type = 'file';
      input.accept = 'image/*';
      input.onchange = () => {
        if (input.files && input.files[0]) {
          const reader = new FileReader();
          reader.onload = () => {
            const imgEl: ImageElement = {
              id: `el-${Date.now()}`,
              type: 'image',
              layerId: activeLayerId,
              x: 0, y: 0,
              width: 200,
              height: 200,
              properties: { src: reader.result as string },
            };
            setElements((prev) => [...prev, imgEl]);
            setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Bild eingefügt', type: 'info' }]);
            // Push image element to Yjs CRDT for real-time sync
            collab.setElement(imgEl);
            // Save to backend
            if (drawingId && token) {
              setSavedStatus('Speichert…');
              createElementTyped(token, drawingId, imgEl).then(() => {
                setSavedStatus('gespeichert');
              }).catch((err) => {
                console.error('Failed to save image element:', err);
                setSavedStatus('Fehler beim Speichern');
              });
            }
          };
          reader.readAsDataURL(input.files[0]);
        }
      };
      input.click();
      return;
    }

    // ─── Format actions ───
    if (action.startsWith('format-')) {
      const formatType = action.replace('format-', '');
      const selectedIds = selectedElementIdsRef.current;
      const selected = selectedIds.length > 0
        ? elements.filter(e => selectedIds.includes(e.id))
        : (selectedElement ? [selectedElement] : []);
      if (selected.length === 0) {
        setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Format: Keine Elemente ausgewählt', type: 'info' }]);
        return;
      }
      const modified = selected.map(el => {
        const props = { ...el.properties };
        switch (formatType) {
          case 'stroke-width':
            props.strokeWidth = (props.strokeWidth ?? 1) + 0.5;
            break;
          case 'stroke-color':
            props.stroke = props.stroke === '#ff0000' ? '#00ff00' : '#ff0000';
            break;
          case 'fill-color':
            props.fill = props.fill === '#ff0000' ? '#00ff00' : '#ff0000';
            break;
          case 'stroke-style':
            props.lineType = props.lineType === 'solid' ? 'dashed' : 'solid';
            break;
          case 'align-left':
            return { ...el, x: selected.reduce((min, s) => Math.min(min, s.x), Infinity) };
          case 'align-center':
            const cx = selected.reduce((sum, s) => sum + s.x, 0) / selected.length;
            return { ...el, x: cx };
          case 'align-right':
            return { ...el, x: selected.reduce((max, s) => Math.max(max, s.x), -Infinity) };
          default:
            return el;
        }
        return { ...el, properties: props };
      });
      handleElementsModified(modified);
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Format: ${formatType} auf ${modified.length} Element(e) angewendet`, type: 'info' }]);
      return;
    }

    // ─── View actions ───
    if (action === 'zoom-fit') {
      setZoomCommand('fit');
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: an Ansicht angepasst', type: 'info' }]);
      return;
    }
    if (action === 'zoom-100') {
      setZoomCommand('100');
      setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: 100%', type: 'info' }]);
      return;
    }
    if (action === 'grid') {
      setGridEnabled((p) => !p);
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Grid ${gridEnabled ? 'aus' : 'ein'}`, type: 'info' }]);
      return;
    }
    if (action === 'layer') { setActiveRightPanel('layer'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Layer-Manager geöffnet', type: 'info' }]); return; }

    // ─── Tools actions ───
    if (action === 'measure') { setActiveTool('dimension'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Messwerkzeug aktiv', type: 'info' }]); return; }
    if (action === 'history') { setHistoryPanelOpen((prev) => !prev); return; }

    // ─── Background ───
    if (action === 'background-import') { setBgImportOpen(true); return; }

    // ─── KI actions ───
    if (action === 'ki') { setActiveRightPanel('ki'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'KI Copilot geöffnet', type: 'info' }]); return; }
    if (action === 'ki-draw') { setActiveRightPanel('ki'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'KI Zeichnen: Beschreibung eingeben', type: 'info' }]); return; }
    if (action === 'ki-analyze') { setActiveRightPanel('ki'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'KI Analyse gestartet', type: 'info' }]); return; }
  }, [handleUndo, handleRedo, handleElementsModified, handleImport, handleExport, onNavigateBack, drawingId, token, elements, activeLayerId, gridEnabled, collab, selectedElement]);

  const handleToolChange = useCallback((tool: string) => {
    setActiveTool(tool);
    setMobileLeftOpen(false);
  }, []);

  const handleViewChange = useCallback((mode: ViewMode) => {
    setViewMode(mode);
  }, []);

  const handleToggleGrid = useCallback(() => setGridEnabled((p) => !p), []);
  const handleToggleOrtho = useCallback(() => setOrthoEnabled((p) => !p), []);
  const handleToggleSnap = useCallback(() => setSnapEnabled((p) => !p), []);
  const handleTogglePolar = useCallback(() => setPolarEnabled((p) => !p), []);

  // Layer handlers
  const handleSelectLayer = useCallback((id: string) => setActiveLayerId(id), []);
  const handleAddLayer = useCallback(() => {
    setLayers((prev) => {
      const newId = `layer-${Date.now()}`;
      const newLayer: CADLayer = {
        id: newId, name: `Layer ${prev.length + 1}`, visible: true, locked: false,
        color: '#ffffff', lineType: 'solid', transparency: 0,
        sortOrder: prev.length, parentId: null,
      };
      // Save to backend
      if (drawingId && token) {
        createLayerTyped(token, drawingId, newLayer).catch((err) => {
          console.error('Failed to save layer:', err);
        });
      }
      return [...prev, newLayer];
    });
  }, [drawingId, token]);
  const handleToggleLayer = useCallback((id: string) => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, visible: !l.visible } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { visible: changed.visible }).catch((err) => {
            console.error('Failed to save layer visibility:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);
  const handleDeleteLayer = useCallback((id: string) => {
    setLayers((prev) => prev.filter((l) => l.id !== id));
    // Delete from backend
    if (token) {
      apiDeleteLayer(token, id).catch((err) => {
        console.error('Failed to delete layer:', err);
      });
    }
  }, [token]);
  const handleRenameLayer = useCallback((id: string, name: string) => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, name } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { name }).catch((err) => {
            console.error('Failed to save layer rename:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);
  const handleDuplicateLayer = useCallback((id: string) => {
    setLayers((prev) => {
      const layer = prev.find((l) => l.id === id);
      if (!layer) return prev;
      const newId = `layer-${Date.now()}`;
      const copy: CADLayer = { ...layer, id: newId, name: `${layer.name} (Kopie)`, sortOrder: prev.length };
      collab.setLayer(copy);
      if (drawingId && token) {
        createLayerTyped(token, drawingId, copy).catch((err) => {
          console.error('Failed to save duplicated layer:', err);
        });
      }
      return [...prev, copy];
    });
  }, [drawingId, token, collab]);
  const handleToggleLock = useCallback((id: string) => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, locked: !l.locked } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { locked: changed.locked }).catch((err) => {
            console.error('Failed to save layer lock state:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);
  const handleUpdateLayerColor = useCallback((id: string, color: string) => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, color } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { color }).catch((err) => {
            console.error('Failed to save layer color:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);
  const handleUpdateLayerLineType = useCallback((id: string, lineType: 'solid' | 'dashed' | 'dotted') => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, lineType } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { lineType }).catch((err) => {
            console.error('Failed to save layer line type:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);
  const handleUpdateLayerTransparency = useCallback((id: string, transparency: number) => {
    setLayers((prev) => {
      const updated = prev.map((l) => l.id === id ? { ...l, transparency } : l);
      const changed = updated.find((l) => l.id === id);
      if (changed) {
        collab.setLayer(changed);
        if (token) {
          updateLayer(token, id, { transparency }).catch((err) => {
            console.error('Failed to save layer transparency:', err);
          });
        }
      }
      return updated;
    });
  }, [token, collab]);

  const handleZoomIn = useCallback(() => {
    setZoomCommand('in');
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: vergrößert', type: 'info' }]);
  }, []);
  const handleZoomOut = useCallback(() => {
    setZoomCommand('out');
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: verkleinert', type: 'info' }]);
  }, []);
  const handleZoomFit = useCallback(() => {
    setZoomCommand('fit');
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: an Ansicht angepasst', type: 'info' }]);
  }, []);

  // Layer reorder (drag & drop)
  const handleReorderLayer = useCallback((draggedId: string, targetId: string, position: 'before' | 'after' | 'inside') => {
    setLayers((prev) => {
      const dragged = prev.find((l) => l.id === draggedId);
      if (!dragged) return prev;

      if (position === 'inside') {
        // Make dragged a child of target
        const updated = prev.map((l) =>
          l.id === draggedId ? { ...l, parentId: targetId } : l
        );
        return updated;
      }

      // before/after: reorder at same level
      const target = prev.find((l) => l.id === targetId);
      if (!target) return prev;

      const parentId = target.parentId;
      const siblings = prev
        .filter((l) => l.parentId === parentId && l.id !== draggedId)
        .sort((a, b) => a.sortOrder - b.sortOrder);

      const targetIdx = siblings.findIndex((l) => l.id === targetId);
      const insertIdx = position === 'before' ? targetIdx : targetIdx + 1;

      siblings.splice(insertIdx, 0, { ...dragged, parentId });

      // Reassign sortOrder
      const reordered = siblings.map((l, i) => ({ ...l, sortOrder: i }));

      // Merge back: non-siblings keep original, siblings get reordered
      const nonSiblings = prev.filter((l) => l.parentId !== parentId || l.id === draggedId);
      const result = [...nonSiblings.filter((l) => l.id !== draggedId), ...reordered];
      return result;
    });
    setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Ebene verschoben', type: 'info' }]);
  }, []);

  // Add sub-layer
  const handleAddSubLayer = useCallback((parentId: string) => {
    setLayers((prev) => {
      const parent = prev.find((l) => l.id === parentId);
      if (!parent) return prev;
      const newId = `layer-${Date.now()}`;
      const siblings = prev.filter((l) => l.parentId === parentId);
      const newLayer: CADLayer = {
        id: newId, name: `Teilebene ${siblings.length + 1}`, visible: true, locked: false,
        color: parent.color, lineType: 'solid', transparency: 0,
        sortOrder: siblings.length, parentId,
      };
      if (drawingId && token) {
        createLayerTyped(token, drawingId, newLayer).catch((err) => {
          console.error('Failed to save sub-layer:', err);
        });
      }
      return [...prev, newLayer];
    });
  }, [drawingId, token]);

  // Update single element (from properties panel)
  const handleUpdateElement = useCallback((updated: CADElement) => {
    setElements((prev) => {
      const newElements = prev.map((e) => (e.id === updated.id ? updated : e));
      historyManagerRef.current.pushSnapshot({
        elements: newElements, layers, blocks, groups, bgConfig,
      }, 'Element geändert');
      return newElements;
    });
    syncHistory();
    collab.setElement(updated);
    if (token) {
      setSavedStatus('Speichert…');
      updateElement(token, updated.id, updated).then(() => {
        setSavedStatus('gespeichert');
      }).catch((err) => {
        console.error('Failed to update element:', err);
        setSavedStatus('Fehler beim Speichern');
      });
    }
    setSelectedElement(updated);
  }, [layers, blocks, groups, bgConfig, syncHistory, token, collab]);

  const handleBgApply = useCallback((config: BackgroundConfig, _image: HTMLImageElement | null) => {
    setBgConfig(config);
    setBgImportOpen(false);
    setCommandHistory((prev) => [...prev, { prefix: '·', text: `Hintergrund geladen: ${config.name} (${config.width}×${config.height}px, Maßstab: ${config.scale.toFixed(3)} px/mm)`, type: 'info' }]);
  }, []);

  const handleCommand = useCallback((cmd: string) => {
    const upper = cmd.trim().toUpperCase();
    setCommandHistory((prev) => [...prev, { prefix: '›', text: cmd, type: 'command' }]);

    const registry = getCommandRegistry();

    if (upper === 'UNDO' || upper === 'U') {
      handleUndo();
      return;
    }
    if (upper === 'REDO') {
      handleRedo();
      return;
    }

    // Group command: create group from currently selected elements
    if (upper === 'GROUP' || upper === 'GRP') {
      const gm = groupManagerRef.current;
      const ids = selectedElementIdsRef.current;
      if (ids.length < 2) {
        setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Gruppe: Mindestens 2 Elemente auswählen', type: 'info' }]);
        return;
      }
      const group = gm.createGroup(ids);
      const newGroups = gm.getGroups();
      setGroups(newGroups);
      // Sync group to Yjs CRDT for real-time collaboration
      collab.setGroup(group);
      setCommandHistory((prev) => [...prev, { prefix: '·', text: `Gruppe erstellt: ${group.name} (${ids.length} Elemente)`, type: 'info' }]);
      return;
    }

    // Ungroup command
    if (upper === 'UNG' || upper === 'UNGROUP') {
      const gm = groupManagerRef.current;
      const allGroups = gm.getGroups();
      if (allGroups.length > 0) {
        gm.ungroup(allGroups[allGroups.length - 1].id);
        setGroups(gm.getGroups());
        setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Gruppe aufgelöst', type: 'info' }]);
      }
      return;
    }

    // Import command — trigger file dialog
    if (upper === 'IMPORT' || upper === 'IMP' || upper === 'I') {
      handleRibbonAction('import');
      return;
    }
    // Export command — trigger export dialog
    if (upper === 'EXPORT' || upper === 'EXP' || upper === 'EX') {
      handleRibbonAction('export');
      return;
    }

    const tool = registry.getToolId(upper);
    if (tool) {
      setActiveTool(tool);
      setMobileLeftOpen(false);
      const label = registry.getLabel(upper) ?? `Werkzeug: ${tool}`;
      setCommandHistory((prev) => [...prev, { prefix: '·', text: label, type: 'info' }]);
    } else {
      // Check plugin commands
      const pluginCmds = pluginRegistry.getCommandExtensions();
      const parts = cmd.trim().split(/\s+/);
      const cmdName = parts[0].toUpperCase();
      const pluginCmd = pluginCmds.find((c) => c.name.toUpperCase() === cmdName);
      if (pluginCmd) {
        pluginCmd.execute(parts.slice(1), createPluginContext());
      } else {
        setCommandHistory((prev) => [...prev, { prefix: '·', text: `Unbekannter Befehl: ${cmd}`, type: 'info' }]);
      }
    }
  }, [handleUndo, handleRedo, handleRibbonAction, collab, createPluginContext]);

  const handleKISend = useCallback(async (text: string) => {
    const userMsg: KIMessage = { id: `ki-${Date.now()}`, role: 'user', content: text };
    const pendingId = `ki-${Date.now() + 1}`;
    const pendingMsg: KIMessage = { id: pendingId, role: 'assistant', content: '…' };
    setKIMessages((prev) => [...prev, userMsg, pendingMsg]);
    setKiLoading(true);

    try {
      // Build CAD context
      const elementTypeSummary: Record<string, number> = {};
      for (const el of elements) {
        elementTypeSummary[el.type] = (elementTypeSummary[el.type] || 0) + 1;
      }
      const context = {
        projectName,
        elementCount: elements.length,
        layerCount: layers.length,
        elementTypeSummary,
      };

      // Build message history (last 10 messages)
      const history = kiMessages.slice(-10).map((m) => ({
        role: m.role,
        content: typeof m.content === 'string' ? m.content : String(m.content),
      }));
      history.push({ role: 'user', content: text });

      const result = await aiChat(token, history, context);

      setKIMessages((prev) =>
        prev.map((m) =>
          m.id === pendingId
            ? { ...m, content: result.content }
            : m
        )
      );
    } catch (err: any) {
      setKIMessages((prev) =>
        prev.map((m) =>
          m.id === pendingId
            ? { ...m, content: `Fehler: ${err.message || 'KI-Anfrage fehlgeschlagen'}` }
            : m
        )
      );
    } finally {
      setKiLoading(false);
    }
  }, [token, projectName, elements, layers, kiMessages]);

  const handleSuggestionClick = useCallback((suggestion: KISuggestion) => {
    handleKISend(suggestion.label);
  }, [handleKISend]);

  const activeLayerName = layers.find((l) => l.id === activeLayerId)?.name ?? '—';

  // ─── Render ─────────────────────────────────────────────
  return (
    <div className={`app ${theme}`} data-theme={theme}>
      <Topbar
        projectName={projectName}
        savedStatus={savedStatus}
        onUndo={handleUndo}
        onRedo={handleRedo}
        onThemeToggle={handleThemeToggle}
        theme={theme}
        onOpenSettings={() => setSettingsOpen(true)}
        onOpenLeftDrawer={() => setMobileLeftOpen(true)}
        onOpenRightDrawer={() => setMobileRightOpen(true)}
      />
      <RibbonBar
        activeTab={activeRibbonTab}
        onTabChange={setActiveRibbonTab}
        onAction={handleRibbonAction}
      />
      <div className={`app-body${leftSidebarCollapsed ? ' left-collapsed' : ''}${rightSidebarCollapsed ? ' right-collapsed' : ''}`}>
        <LeftSidebar
          activeTool={activeTool}
          onToolChange={handleToolChange}
          selectedTemplate={selectedTemplate}
          onTemplateSelect={handleTemplateSelect}
          className={mobileLeftOpen ? 'open' : ''}
          onCollapse={() => setMobileLeftOpen(false)}
          collapsed={leftSidebarCollapsed}
          onToggleCollapse={() => setLeftSidebarCollapsed((p) => !p)}
          blocks={blocks}
          onDragBlock={handleDragBlock}
          onBlockCategoryChange={handleBlockCategoryChange}
          onBlockSearch={handleBlockSearch}
          onRenameBlock={handleRenameBlock}
          onDuplicateBlock={handleDuplicateBlock}
          onDeleteBlock={handleDeleteBlock}
          onSvgImport={handleSvgImport}
          onSaveGroupAsBlock={handleSaveGroupAsBlock}
        />
        <CanvasArea
          cursorPos={cursorPos}
          viewMode={viewMode}
          onViewChange={handleViewChange}
          gridEnabled={gridEnabled}
          orthoEnabled={orthoEnabled}
          snapEnabled={snapEnabled}
          polarEnabled={polarEnabled}
          activeTool={activeTool}
          elements={elements}
          layers={layers}
          onElementCreated={handleElementCreated}
          onElementsDeleted={handleElementsDeleted}
          onElementsModified={handleElementsModified}
          onCursorMoved={handleCursorMoved}
          onToolStateChanged={handleToolStateChanged}
          onToggleGrid={handleToggleGrid}
          onToggleOrtho={handleToggleOrtho}
          onToggleSnap={handleToggleSnap}
          onZoomIn={handleZoomIn}
          onZoomOut={handleZoomOut}
          onZoomFit={handleZoomFit}
          zoomCommand={zoomCommand}
          onTextEdit={handleTextEdit}
          onCommandTrigger={handleCommandTrigger}
          blocks={blocks}
          onBlockDrop={handleBlockDrop}
          onSelectionChange={handleSelectionChange}
          selectedTemplate={selectedTemplate}
          bgConfig={bgConfig}
          remoteCursors={collab.cursors}
        />
        <RightSidebar
          activePanel={activeRightPanel}
          onPanelChange={setActiveRightPanel}
          selectedElement={selectedElement}
          layers={layers}
          blocks={blocks}
          activeLayerId={activeLayerId}
          onSelectLayer={handleSelectLayer}
          onAddLayer={handleAddLayer}
          onToggleLayer={handleToggleLayer}
          onDeleteLayer={handleDeleteLayer}
          onRenameLayer={handleRenameLayer}
          onDuplicateLayer={handleDuplicateLayer}
          onToggleLock={handleToggleLock}
          onElementsDeleted={handleElementsDeleted}
          onToggleElementVisible={handleToggleElementVisible}
          elements={elements}
          onRenameBlock={handleRenameBlock}
          onDuplicateBlock={handleDuplicateBlock}
          onDeleteBlock={handleDeleteBlock}
          onSvgImport={handleSvgImport}
          onSaveGroupAsBlock={handleSaveGroupAsBlock}
          onBlockCategoryChange={handleBlockCategoryChange}
          onBlockSearch={handleBlockSearch}
          onDragBlock={handleDragBlock}
          kiMessages={kiMessages}
          kiSuggestions={kiSuggestions}
          onKISend={handleKISend}
          onKISuggestionClick={handleSuggestionClick}
          kiLoading={kiLoading}
          className={mobileRightOpen ? 'open' : ''}
          onCollapse={() => setMobileRightOpen(false)}
          collapsed={rightSidebarCollapsed}
          onToggleCollapse={() => setRightSidebarCollapsed((p) => !p)}
          onReorder={handleReorderLayer}
          onAddSubLayer={handleAddSubLayer}
          onUpdateElement={handleUpdateElement}
        />
      </div>
      <CommandLine
        history={commandHistory}
        onCommand={handleCommand}
      />
      <StatusBar
        snapEnabled={snapEnabled}
        orthoEnabled={orthoEnabled}
        polarEnabled={polarEnabled}
        gridEnabled={gridEnabled}
        cursorX={cursorPos.x}
        cursorY={cursorPos.y}
        activeLayer={activeLayerName}
        activeTool={activeTool}
        onlineCount={onlineCount}
        onToggleSnap={handleToggleSnap}
        onToggleOrtho={handleToggleOrtho}
        onTogglePolar={handleTogglePolar}
        onToggleGrid={handleToggleGrid}
        seatCount={seatCount}
      />
      <MobileDrawers
        leftOpen={mobileLeftOpen}
        rightOpen={mobileRightOpen}
        activeRightTab={activeDrawerTab}
        onCloseLeft={() => setMobileLeftOpen(false)}
        onCloseRight={() => setMobileRightOpen(false)}
        onRightTabChange={setActiveDrawerTab}
      />
      <SettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} />
      <BackgroundImport
        open={bgImportOpen}
        onClose={() => setBgImportOpen(false)}
        onApply={handleBgApply}
        backgroundService={bgServiceRef.current}
      />
      {historyPanelOpen && (
        <HistoryPanel
          entries={historyEntries}
          onJumpTo={(entryId: string) => {
            const snap = historyManagerRef.current.jumpTo(entryId);
            if (snap) {
              restoreSnapshot(snap);
              syncHistory();
              setCommandHistory((prev) => [...prev, { prefix: '·', text: `Historie: zu „${snap.label}“ gesprungen`, type: 'info' }]);
            }
          }}
          onClose={() => setHistoryPanelOpen(false)}
        />
      )}
      {exportFormatOpen && (
        <div className="export-format-overlay" onClick={() => setExportFormatOpen(false)}>
          <div className="export-format-modal" onClick={(e) => e.stopPropagation()}>
            <h3>Export als…</h3>
            <div className="export-format-buttons">
              {(['dxf', 'svg', 'pdf', 'png', 'json'] as ExportFormat[]).map(fmt => (
                <button
                  key={fmt}
                  className="export-format-btn"
                  onClick={() => {
                    setExportFormatOpen(false);
                    handleExport(fmt);
                  }}
                >
                  {fmt.toUpperCase()}
                </button>
              ))}
            </div>
            <button className="export-format-cancel" onClick={() => setExportFormatOpen(false)}>Abbrechen</button>
          </div>
        </div>
      )}
      {editingTextElement && (
        <InlineTextEditor
          initialText={editingTextElement.properties?.text ?? ''}
          onSubmit={handleTextEditorSubmit}
          onCancel={handleTextEditorCancel}
        />
      )}
    </div>
  );
};

// ─── App Wrapper (Auth Gate) ───────────────────────────
const App: React.FC = () => {
  const { user, token } = useAuth();
  const [authView, setAuthView] = useState<'login' | 'register'>('login');
  const [openedProjectId, setOpenedProjectId] = useState<string | null>(null);

  // Not authenticated → show Login or Register
  if (!user || !token) {
    return authView === 'login'
      ? <Login onSwitchToRegister={() => setAuthView('register')} />
      : <Register onSwitchToLogin={() => setAuthView('login')} />;
  }

  // Authenticated but no project opened → show Dashboard
  if (!openedProjectId) {
    return <Dashboard onOpenProject={(id) => setOpenedProjectId(id)} />;
  }

  // Authenticated + project opened → show CAD Editor
  return <CADEditor projectId={openedProjectId} token={token} onNavigateBack={() => setOpenedProjectId(null)} />;
};

export default App;
