import React, { useRef, useEffect, useState } from 'react';
import type { CanvasAreaProps, ViewMode } from '../types/ui.types';
import type { CADElement } from '../types/cad.types';
import type { ToolType } from '../types/cad.types';
import type { UserCursor } from '../crdt';
import { RenderEngine } from '../canvas/RenderEngine';
import { ZoomPanController } from '../canvas/ZoomPanController';
import { InteractionEngine } from '../interaction';
import { SnapEngine } from '../canvas/SnapEngine';
import { SelectionEngine } from '../canvas/SelectionEngine';
import { SpatialIndex } from '../canvas/SpatialIndex';
import { LayerManager } from '../canvas/LayerManager';
import { GroupManager } from '../tools/modification/GroupTool';

const CanvasArea: React.FC<CanvasAreaProps> = ({
  cursorPos, viewMode, onViewChange, gridEnabled, orthoEnabled, snapEnabled,
  polarEnabled,
  activeTool, elements, layers, activeLayerId, onElementCreated, onElementsDeleted, onElementsModified, onCursorMoved, onToolStateChanged,
  onToggleGrid, onToggleOrtho, onToggleSnap, onZoomIn, onZoomOut, onZoomFit, onTextEdit, onCommandTrigger, blocks, onBlockDrop, onSelectionChange, selectedTemplate, bgConfig, remoteCursors, zoomCommand,
  groups,
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const zoomPanRef = useRef<ZoomPanController | null>(null);
  const renderEngineRef = useRef<RenderEngine | null>(null);
  const interactionRef = useRef<InteractionEngine | null>(null);
  const spatialIndexRef = useRef<SpatialIndex | null>(null);
  const layerManagerRef = useRef<LayerManager | null>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const zoomPan = new ZoomPanController(canvas);
    const spatialIndex = new SpatialIndex();
    const layerManager = new LayerManager();
    const renderEngine = new RenderEngine(canvas, zoomPan, spatialIndex, layerManager);
    const snapEngine = new SnapEngine();
    const selectionEngine = new SelectionEngine(renderEngine, spatialIndex, layerManager);
    const interaction = new InteractionEngine(
      canvas, zoomPan, renderEngine, snapEngine, selectionEngine, spatialIndex, layerManager,
    );

    zoomPanRef.current = zoomPan;
    renderEngineRef.current = renderEngine;
    interactionRef.current = interaction;
    spatialIndexRef.current = spatialIndex;
    layerManagerRef.current = layerManager;

    interaction.setCallbacks({
      onElementCreated,
      onElementsDeleted,
      onElementsModified,
      onCursorMoved,
      onToolStateChanged,
      onTextEdit,
      onCommandTrigger,
      onSelectionChange,
    });

    interaction.attach();

    return () => {
      interaction.detach();
      zoomPanRef.current = null;
      renderEngineRef.current = null;
      interactionRef.current = null;
      spatialIndexRef.current = null;
      layerManagerRef.current = null;
    };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Update callbacks when they change (avoids stale closures)
  useEffect(() => {
    const interaction = interactionRef.current;
    if (!interaction) return;
    interaction.setCallbacks({
      onElementCreated,
      onElementsDeleted,
      onElementsModified,
      onCursorMoved,
      onToolStateChanged,
      onTextEdit,
      onCommandTrigger,
      onSelectionChange,
    });
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [onElementCreated, onElementsDeleted, onElementsModified, onCursorMoved, onToolStateChanged, onTextEdit, onCommandTrigger, onSelectionChange]);

  // Resize canvas to fill container
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const container = canvas.parentElement;
    if (!container) return;

    const resize = () => {
      const w = container.clientWidth;
      const h = container.clientHeight;
      if (w > 0 && h > 0) {
        canvas.width = w;
        canvas.height = h;
        renderEngineRef.current?.render();
      }
    };

    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(container);
    window.addEventListener('resize', resize);

    return () => {
      ro.disconnect();
      window.removeEventListener('resize', resize);
    };
  }, []);

  // Sync elements to interaction engine + spatial index + render
  useEffect(() => {
    const interaction = interactionRef.current;
    const spatialIndex = spatialIndexRef.current;
    const renderEngine = renderEngineRef.current;
    if (!interaction || !spatialIndex || !renderEngine) return;
    interaction.setElements(elements);
    spatialIndex.clear();
    spatialIndex.bulkInsert(elements);
    renderEngine.render();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [elements]);

  // Sync layers to LayerManager + render
  useEffect(() => {
    const renderEngine = renderEngineRef.current;
    const layerManager = layerManagerRef.current;
    if (!renderEngine) return;
    if (layerManager) {
      layerManager.clear();
      layers.forEach(l => layerManager.addLayer(l));
    }
    renderEngine.setLayers(layers);
    renderEngine.render();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [layers]);

  // Sync blocks to RenderEngine
  useEffect(() => {
    const renderEngine = renderEngineRef.current;
    if (!renderEngine || !blocks) return;
    renderEngine.setBlockDefinitions(blocks);
    renderEngine.render();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [blocks]);

  // Sync groups to RenderEngine and InteractionEngine
  useEffect(() => {
    const renderEngine = renderEngineRef.current;
    const interaction = interactionRef.current;
    if (!renderEngine) return;
    renderEngine.setGroups(groups ?? []);
    renderEngine.render();
    // InteractionEngine uses GroupManager from App — we pass it via a simple adapter
    if (interaction && groups) {
      const gm = new GroupManager();
      gm.fromJSON(groups);
      interaction.setGroupManager(gm);
    } else if (interaction) {
      interaction.setGroupManager(null);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [groups]);

  // Sync active layer to LayerManager
  useEffect(() => {
    const layerManager = layerManagerRef.current;
    if (!layerManager || !activeLayerId) return;
    layerManager.setActiveLayer(activeLayerId);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeLayerId, layers]);

  // Sync grid/snap/ortho toggles
  useEffect(() => {
    const renderEngine = renderEngineRef.current;
    const interaction = interactionRef.current;
    if (!renderEngine || !interaction) return;
    renderEngine.setOptions({ showGrid: gridEnabled });
    renderEngine.setOptions({ showSnapPoints: snapEnabled });
    renderEngine.setOptions({ showOrtho: orthoEnabled });
    interaction.setSnapEnabled(snapEnabled);
    interaction.setOrthoEnabled(orthoEnabled);
    interaction.setPolarEnabled(polarEnabled);
    renderEngine.render();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [gridEnabled, snapEnabled, orthoEnabled, polarEnabled]);

  // Sync active tool
  useEffect(() => {
    const interaction = interactionRef.current;
    if (!interaction) return;
    interaction.setTool(activeTool as ToolType);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeTool]);

  // Sync selected template to interaction engine
  useEffect(() => {
    const interaction = interactionRef.current;
    if (!interaction) return;
    interaction.setSelectedTemplate(selectedTemplate ?? null);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedTemplate]);

  // Compute screen positions for remote cursors
  const [cursorScreenPositions, setCursorScreenPositions] = useState<Array<{ cursor: UserCursor; sx: number; sy: number }>>([]);
  useEffect(() => {
    const zoomPan = zoomPanRef.current;
    if (!zoomPan || !remoteCursors || remoteCursors.length === 0) {
      setCursorScreenPositions([]);
      return;
    }
    const positions = remoteCursors
      .filter((c) => c.visible)
      .map((cursor) => {
        const screen = zoomPan.worldToScreen(cursor.x, cursor.y);
        return { cursor, sx: screen.x, sy: screen.y };
      });
    setCursorScreenPositions(positions);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [remoteCursors]);

  // Re-render cursor overlay when canvas renders (zoom/pan changes)
  useEffect(() => {
    const renderEngine = renderEngineRef.current;
    const zoomPan = zoomPanRef.current;
    if (!renderEngine || !zoomPan || !remoteCursors || remoteCursors.length === 0) return;
    const origRender = renderEngine.render.bind(renderEngine);
    renderEngine.render = () => {
      origRender();
      const positions = remoteCursors
        .filter((c) => c.visible)
        .map((cursor) => {
          const screen = zoomPan.worldToScreen(cursor.x, cursor.y);
          return { cursor, sx: screen.x, sy: screen.y };
        });
      setCursorScreenPositions(positions);
    };
    return () => { renderEngine.render = origRender; };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [remoteCursors]);

  const handleZoomIn = () => {
    const zoomPan = zoomPanRef.current;
    const canvas = canvasRef.current;
    if (zoomPan && canvas) {
      zoomPan.zoomAt(canvas.width / 2, canvas.height / 2, 1.2);
      renderEngineRef.current?.render();
    }
    onZoomIn();
  };

  const handleZoomOut = () => {
    const zoomPan = zoomPanRef.current;
    const canvas = canvasRef.current;
    if (zoomPan && canvas) {
      zoomPan.zoomAt(canvas.width / 2, canvas.height / 2, 0.8);
      renderEngineRef.current?.render();
    }
    onZoomOut();
  };

  const handleZoomFit = () => {
    const interaction = interactionRef.current;
    if (interaction) {
      interaction.zoomFit();
    }
    onZoomFit();
  };

  // Handle zoom commands from App (ribbon/menu actions)
  useEffect(() => {
    if (!zoomCommand) return;
    const zoomPan = zoomPanRef.current;
    const canvas = canvasRef.current;
    if (!zoomPan || !canvas) return;
    switch (zoomCommand) {
      case 'in':
        zoomPan.zoomAt(canvas.width / 2, canvas.height / 2, 1.2);
        break;
      case 'out':
        zoomPan.zoomAt(canvas.width / 2, canvas.height / 2, 0.8);
        break;
      case 'fit':
        zoomPan.zoomFit(elements);
        break;
      case '100':
        zoomPan.reset();
        break;
    }
    renderEngineRef.current?.render();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [zoomCommand]);

  return (
    <section className="canvas-area" id="canvas-area" aria-label="CAD Zeichenfläche">
      <div className="canvas-coords" role="status" aria-live="polite">
        <span><span className="canvas-coords-label">X</span><span className="canvas-coords-val" id="coord-x">{cursorPos.x.toFixed(3)}</span></span>
        <span><span className="canvas-coords-label">Y</span><span className="canvas-coords-val" id="coord-y">{cursorPos.y.toFixed(3)}</span></span>
        <span style={{ color: 'var(--color-text-faint)' }}>m</span>
      </div>

      <canvas
        ref={canvasRef}
        className="canvas-svg"
        role="img"
        aria-label="CAD Zeichenfläche"
        onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }}
        onDrop={(e) => {
          e.preventDefault();
          const rect = e.currentTarget.getBoundingClientRect();
          const sx = e.clientX - rect.left;
          const sy = e.clientY - rect.top;
          const zoomPan = zoomPanRef.current;
          if (!zoomPan) return;
          const world = zoomPan.screenToWorld(sx, sy);
          // Project block drop
          const blockId = e.dataTransfer.getData('text/block-id');
          if (blockId && onBlockDrop) {
            onBlockDrop(blockId, world.x, world.y);
            return;
          }
          // Global block drop — parse block_data and create element(s)
          const globalBlockData = e.dataTransfer.getData('text/global-block-data');
          if (globalBlockData) {
            try {
              const parsed = JSON.parse(globalBlockData);
              if (parsed.type === 'svg' && parsed.svg) {
                // SVG block — create a block_instance element
                const svgEl: CADElement = {
                  id: `el_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
                  type: 'block_instance',
                  layerId: 'layer-0',
                  x: world.x,
                  y: world.y,
                  width: 100,
                  height: 100,
                  properties: { blockId: `svg_${Date.now()}`, svgData: parsed.svg, scale: 1 },
                };
                onElementCreated(svgEl);
              } else if (Array.isArray(parsed)) {
                // Array of elements — create them with offset
                parsed.forEach((el: CADElement, i: number) => {
                  onElementCreated({
                    ...el,
                    id: `el_${Date.now()}_${i}`,
                    x: (el.x ?? 0) + world.x,
                    y: (el.y ?? 0) + world.y,
                  });
                });
              } else if (parsed.type) {
                // Single element
                onElementCreated({
                  ...parsed,
                  id: `el_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
                  x: world.x,
                  y: world.y,
                });
              }
            } catch {
              // Invalid block data — ignore
            }
          }
        }}
      />

      {cursorScreenPositions.map(({ cursor, sx, sy }) => (
        <div
          key={cursor.userId}
          className="remote-cursor"
          style={{
            position: 'absolute',
            left: `${sx}px`,
            top: `${sy}px`,
            pointerEvents: 'none',
            zIndex: 1000,
          }}
        >
          <div
            style={{
              width: '12px',
              height: '12px',
              borderRadius: '50%',
              backgroundColor: cursor.color,
              border: '2px solid white',
              boxShadow: '0 0 4px rgba(0,0,0,0.5)',
            }}
          />
          <div
            style={{
              position: 'absolute',
              left: '16px',
              top: '8px',
              backgroundColor: cursor.color,
              color: 'white',
              padding: '2px 6px',
              borderRadius: '4px',
              fontSize: '11px',
              fontFamily: 'sans-serif',
              whiteSpace: 'nowrap',
              boxShadow: '0 1px 3px rgba(0,0,0,0.4)',
            }}
          >
            {cursor.userName}
          </div>
        </div>
      ))}

      <div className="canvas-toolbar" role="toolbar" aria-label="Canvas-Werkzeuge">
        <button className="canvas-toolbar-btn" title="Herauszoomen (-)" aria-label="Herauszoomen" onClick={handleZoomOut}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
        </button>
        <span className="canvas-zoom-display" id="zoom-display">100%</span>
        <button className="canvas-toolbar-btn" title="Hineinzoomen (+)" aria-label="Hineinzoomen" onClick={handleZoomIn}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
        </button>
        <button className="canvas-toolbar-btn" title="Zoom anpassen (F)" aria-label="Zoom anpassen" onClick={handleZoomFit}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><line x1="7" y1="12" x2="17" y2="12"/></svg>
        </button>
        <div className="canvas-toolbar-divider"></div>
        <button className={`canvas-toolbar-btn${gridEnabled ? ' active' : ''}`} title="Grid ein/aus (F7)" aria-label="Grid umschalten" aria-pressed={gridEnabled} onClick={onToggleGrid}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>
        </button>
        <button className={`canvas-toolbar-btn${orthoEnabled ? ' active' : ''}`} title="Ortho-Modus (F8)" aria-label="Ortho-Modus umschalten" aria-pressed={orthoEnabled} onClick={onToggleOrtho}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l18 18M3 21l18-18"/></svg>
        </button>
        <button className={`canvas-toolbar-btn${snapEnabled ? ' active' : ''}`} title="Snap ein/aus (F3)" aria-label="Snap umschalten" aria-pressed={snapEnabled} onClick={onToggleSnap}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2v4M12 18v4M2 12h4M18 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M5.6 18.4l2.8-2.8M15.6 8.4l2.8-2.8"/><circle cx="12" cy="12" r="3"/></svg>
        </button>
        <div className="canvas-toolbar-divider"></div>
        <button className="canvas-toolbar-btn" title="Vorherige Ansicht" aria-label="Vorherige Ansicht">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
        </button>
      </div>
    </section>
  );
};

export default CanvasArea;
