import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Stage, Layer } from 'react-konva';
import type Konva from 'konva';
import type { Point } from '@house-plan-maker/shared';
import { useZoomPan, useSelection, useSceneData } from './context/EditorContext';
import { useUndoRedo } from './context/UndoRedoContext';
import { useEditorZoom } from './hooks/useEditorZoom';
import { GridLayer } from './layers/GridLayer';
import { WallLayer } from './layers/WallLayer';
import { OpeningLayer, type OpeningPreview } from './layers/OpeningLayer';
import { ElectricalLayer } from './layers/ElectricalLayer';
import { FurnitureLayer } from './layers/FurnitureLayer';
import { SelectionLayer } from './layers/SelectionLayer';
import { MeasurementLayer } from './layers/MeasurementLayer';
import { RoomLabelLayer } from './layers/RoomLabelLayer';
import { ScaleBar } from './overlays/ScaleBar';
import {
  hitTest,
  selectionRect,
  elementsInRect,
  selectedBoundingBox,
} from './tools/SelectTool';
import { computeDoorPreview, createDoorOpening } from './tools/DoorTool';
import { computeWindowPreview, createWindowOpening } from './tools/WindowTool';
import { computeElectricalPreview, createElectricalItemFromPlacement } from './tools/ElectricalTool';
import { computeFurniturePreview, createFurnitureItemFromPlacement } from './tools/FurnitureTool';
import { startMeasurement, updateMeasurement, finishMeasurement } from './tools/MeasureTool';
import { ELECTRICAL_SYMBOL_DEFS } from './symbols/electrical';
import { FURNITURE_DEFS } from './symbols/furniture';
import { useCanvasColors } from './utils/canvasThemeColors';
import { AnnotationLayer } from './layers/AnnotationLayer';
import { MeasureOverlayLayer } from './layers/MeasureOverlayLayer';
import { generateLocalId } from './utils/geometry';
import { TextPromptModal } from '../ui/TextPromptModal';
import type { EditorCommand, MeasurementState } from './types';

interface EditorCanvasProps {
  readonly width: number;
  readonly height: number;
  readonly onStageRef?: (stage: Konva.Stage | null) => void;
}

/** Cursor styles per tool. */
const TOOL_CURSORS: Record<string, string> = {
  select: 'default',
  door: 'crosshair',
  window: 'crosshair',
  electrical: 'crosshair',
  furniture: 'crosshair',
  measure: 'crosshair',
  annotate: 'text',
};

export function EditorCanvas({ width, height, onStageRef }: EditorCanvasProps) {
  const { t } = useTranslation();
  const canvasColors = useCanvasColors();
  const { zoom, panOffset, setZoom, setPanOffset } = useZoomPan();
  const {
    selectedIds,
    activeTool,
    dispatch: selectionDispatch,
    selectElement,
    clearSelection,
  } = useSelection();
  const {
    walls,
    openings,
    electricalItems,
    furnitureItems,
    room,
    gridSize,
    gridVisible,
    layerVisibility,
    selectedElectricalIndex,
    selectedFurnitureIndex,
    annotations,
    globalFurnitureOpacity,
    dispatch: sceneDispatch,
    addOpening,
    addElectrical,
    addFurniture,
    addAnnotation,
    updateAnnotation,
  } = useSceneData();
  const { execute } = useUndoRedo();

  // Use scene dispatch for actions that affect scene data
  const dispatch = sceneDispatch;

  const stageRef = useRef<Konva.Stage | null>(null);

  // Task 5: Fix useEffect missing dependency array
  useEffect(() => {
    onStageRef?.(stageRef.current);
  }, [onStageRef]);

  const {
    handleWheel,
    screenToWorld,
    isPanningRef,
    handlePanStart,
    startLeftMousePan,
    handlePanMove,
    handlePanEnd,
  } = useEditorZoom({
    zoom,
    panOffset,
    onZoomChange: (z) => setZoom(z),
    onPanChange: (offset) => setPanOffset(offset),
  });

  // ── Selection drag state ──
  const [dragRect, setDragRect] = useState<{
    x: number;
    y: number;
    width: number;
    height: number;
  } | null>(null);
  const dragStartRef = useRef<Point | null>(null);
  const isDraggingSelectRef = useRef(false);

  // ── Item drag state (moving selected items) ──
  const isDraggingItemRef = useRef(false);
  const dragItemStartRef = useRef<Point | null>(null);
  const dragItemSnapshotRef = useRef<Map<string, { x: number; y: number }>>(new Map());

  // ── Opening placement preview ──
  const [openingPreview, setOpeningPreview] = useState<OpeningPreview | null>(null);

  // ── Annotation editing modal ──
  const [editingAnnotationId, setEditingAnnotationId] = useState<string | null>(null);
  const editingAnnotation = useMemo(
    () => (editingAnnotationId ? annotations.find((a) => a.id === editingAnnotationId) ?? null : null),
    [editingAnnotationId, annotations],
  );

  // ── Measurement tool state ��─
  const [measureState, setMeasureState] = useState<MeasurementState | null>(null);
  const isMeasuringRef = useRef(false);

  // ── Task 4: Memoize selection bounding box ──
  const selBox = useMemo(
    () =>
      selectedIds.size > 0
        ? selectedBoundingBox(selectedIds, openings, walls, electricalItems, furnitureItems)
        : null,
    [selectedIds, openings, walls, electricalItems, furnitureItems],
  );

  // ── Get world coordinates from a Konva mouse event ──
  const getWorldPoint = useCallback(
    (e: Konva.KonvaEventObject<MouseEvent>): Point => {
      const stage = e.target.getStage();
      const pos = stage?.getPointerPosition();
      if (!pos) return { x: 0, y: 0 };
      return screenToWorld(pos.x, pos.y);
    },
    [screenToWorld],
  );

  // ── Mouse Down ──
  const handleMouseDown = useCallback(
    (e: Konva.KonvaEventObject<MouseEvent>) => {
      // Handle panning first
      handlePanStart(e);
      if (e.evt.button === 1) return; // middle mouse = pan only

      if (e.evt.button !== 0) return; // only left click

      const worldPoint = getWorldPoint(e);

      if (activeTool === 'select') {
        // When furniture is (nearly) fully transparent it's effectively
        // invisible — skip it in hit-testing so clicks fall through to
        // elements underneath instead of selecting a ghost.
        const hittableFurniture = globalFurnitureOpacity < 0.05 ? [] : furnitureItems;
        const hit = hitTest(worldPoint, openings, walls, electricalItems, hittableFurniture);

        if (hit) {
          if (e.evt.shiftKey) {
            // Toggle selection with shift
            if (selectedIds.has(hit.id)) {
              selectionDispatch({ type: 'REMOVE_FROM_SELECTION', id: hit.id });
            } else {
              selectionDispatch({ type: 'ADD_TO_SELECTION', id: hit.id });
            }
          } else {
            if (!selectedIds.has(hit.id)) {
              selectElement(hit.id);
            }
            // Start item drag — snapshot starting positions
            isDraggingItemRef.current = true;
            dragItemStartRef.current = worldPoint;
            const snap = new Map<string, { x: number; y: number }>();
            for (const id of selectedIds) {
              const elec = electricalItems.find((i) => i.id === id);
              if (elec) { snap.set(id, { x: elec.x, y: elec.y }); continue; }
              const furn = furnitureItems.find((i) => i.id === id);
              if (furn) { snap.set(id, { x: furn.x, y: furn.y }); }
            }
            // Include the hit item if not yet in selection
            if (!snap.has(hit.id)) {
              const elec = electricalItems.find((i) => i.id === hit.id);
              if (elec) snap.set(hit.id, { x: elec.x, y: elec.y });
              const furn = furnitureItems.find((i) => i.id === hit.id);
              if (furn) snap.set(hit.id, { x: furn.x, y: furn.y });
            }
            dragItemSnapshotRef.current = snap;
          }
        } else {
          if (e.evt.ctrlKey || e.evt.metaKey) {
            // Ctrl+click on empty space: start drag select rectangle
            if (!e.evt.shiftKey) {
              clearSelection();
            }
            dragStartRef.current = worldPoint;
            isDraggingSelectRef.current = true;
          } else {
            // Left click on empty space: start panning
            clearSelection();
            startLeftMousePan(e);
          }
        }
      } else if (activeTool === 'door') {
        const preview = computeDoorPreview(worldPoint, walls, openings);
        if (preview && preview.isValid) {
          const newOpening = createDoorOpening(
            room.id,
            preview.wall.id,
            preview.positionAlongWall,
            preview.width,
          );
          const cmd: EditorCommand = {
            description: 'Place door',
            execute: () => addOpening(newOpening),
            undo: () => dispatch({ type: 'REMOVE_OPENING', id: newOpening.id }),
          };
          execute(cmd);
        }
      } else if (activeTool === 'window') {
        const preview = computeWindowPreview(worldPoint, walls, openings);
        if (preview && preview.isValid) {
          const newOpening = createWindowOpening(
            room.id,
            preview.wall.id,
            preview.positionAlongWall,
            preview.width,
          );
          const cmd: EditorCommand = {
            description: 'Place window',
            execute: () => addOpening(newOpening),
            undo: () => dispatch({ type: 'REMOVE_OPENING', id: newOpening.id }),
          };
          execute(cmd);
        }
      } else if (activeTool === 'electrical' && selectedElectricalIndex != null) {
        const symbolDef = ELECTRICAL_SYMBOL_DEFS[selectedElectricalIndex];
        if (symbolDef) {
          const preview = computeElectricalPreview(worldPoint, walls, symbolDef);
          if (preview.isValid) {
            const newItem = createElectricalItemFromPlacement(
              room.id,
              preview,
              symbolDef.type,
              symbolDef.variant,
              room.wallHeight,
            );
            const cmd: EditorCommand = {
              description: `Place ${symbolDef.label}`,
              execute: () => addElectrical(newItem),
              undo: () => dispatch({ type: 'REMOVE_ELECTRICAL', id: newItem.id }),
            };
            execute(cmd);
          }
        }
      } else if (activeTool === 'furniture' && selectedFurnitureIndex != null) {
        const furnitureDef = FURNITURE_DEFS[selectedFurnitureIndex];
        if (furnitureDef) {
          const preview = computeFurniturePreview(worldPoint, furnitureDef);
          if (preview.isValid) {
            const newItem = createFurnitureItemFromPlacement(room.id, preview, furnitureDef);
            const cmd: EditorCommand = {
              description: `Place ${furnitureDef.label}`,
              execute: () => addFurniture(newItem),
              undo: () => dispatch({ type: 'REMOVE_FURNITURE', id: newItem.id }),
            };
            execute(cmd);
          }
        }
      } else if (activeTool === 'measure') {
        const ms = startMeasurement(worldPoint);
        setMeasureState(ms);
        isMeasuringRef.current = true;
      } else if (activeTool === 'annotate') {
        const newAnnotation = {
          id: generateLocalId(),
          roomId: room.id,
          x: worldPoint.x,
          y: worldPoint.y,
          text: 'Text',
          fontSize: 14,
          color: '#333333',
        };
        const cmd: EditorCommand = {
          description: 'Place annotation',
          execute: () => addAnnotation(newAnnotation),
          undo: () => dispatch({ type: 'REMOVE_ANNOTATION', id: newAnnotation.id }),
        };
        execute(cmd);
        selectionDispatch({ type: 'SET_SELECTED', ids: new Set([newAnnotation.id]) });
      }
    },
    [
      handlePanStart,
      getWorldPoint,
      activeTool,
      openings,
      walls,
      electricalItems,
      furnitureItems,
      selectedIds,
      selectElement,
      clearSelection,
      selectionDispatch,
      dispatch,
      room.id,
      addOpening,
      addElectrical,
      addFurniture,
      addAnnotation,
      execute,
      selectedElectricalIndex,
      selectedFurnitureIndex,
    ],
  );

  // ── Task 3: RAF-throttled mouse move ──
  const rafIdRef = useRef<number | null>(null);
  const pendingMoveRef = useRef<Konva.KonvaEventObject<MouseEvent> | null>(null);

  const processMouseMove = useCallback(
    (e: Konva.KonvaEventObject<MouseEvent>) => {
      handlePanMove(e);

      if (isPanningRef.current) return;

      const worldPoint = getWorldPoint(e);

      // Move selected items by dragging — use snapshot + total delta from start
      if (isDraggingItemRef.current && dragItemStartRef.current && selectedIds.size > 0) {
        const totalDx = worldPoint.x - dragItemStartRef.current.x;
        const totalDy = worldPoint.y - dragItemStartRef.current.y;

        for (const id of selectedIds) {
          const startPos = dragItemSnapshotRef.current.get(id);
          if (!startPos) continue;

          const elec = electricalItems.find((item) => item.id === id);
          if (elec) {
            dispatch({ type: 'UPDATE_ELECTRICAL', item: { ...elec, x: startPos.x + totalDx, y: startPos.y + totalDy } });
            continue;
          }
          const furn = furnitureItems.find((item) => item.id === id);
          if (furn) {
            dispatch({ type: 'UPDATE_FURNITURE', item: { ...furn, x: startPos.x + totalDx, y: startPos.y + totalDy } });
          }
        }
        return;
      }

      // Drag select rectangle
      if (isDraggingSelectRef.current && dragStartRef.current) {
        const rect = selectionRect(dragStartRef.current, worldPoint);
        setDragRect(rect);
        return;
      }

      // Opening placement preview
      if (activeTool === 'door') {
        const preview = computeDoorPreview(worldPoint, walls, openings);
        setOpeningPreview(
          preview
            ? {
                wall: preview.wall,
                positionAlongWall: preview.positionAlongWall,
                width: preview.width,
                type: 'DOOR',
                isValid: preview.isValid,
              }
            : null,
        );
      } else if (activeTool === 'window') {
        const preview = computeWindowPreview(worldPoint, walls, openings);
        setOpeningPreview(
          preview
            ? {
                wall: preview.wall,
                positionAlongWall: preview.positionAlongWall,
                width: preview.width,
                type: 'WINDOW',
                isValid: preview.isValid,
              }
            : null,
        );
      } else {
        setOpeningPreview(null);
      }

      // Measurement tool: update end point while dragging
      if (activeTool === 'measure' && isMeasuringRef.current && measureState) {
        setMeasureState(updateMeasurement(measureState, worldPoint));
      }
    },
    [handlePanMove, isPanningRef, getWorldPoint, activeTool, walls, openings, measureState, selectedIds, electricalItems, furnitureItems, dispatch],
  );

  const handleMouseMove = useCallback(
    (e: Konva.KonvaEventObject<MouseEvent>) => {
      // Always forward pan and item drag moves immediately for responsiveness
      if (isPanningRef.current) {
        handlePanMove(e);
        return;
      }
      if (isDraggingItemRef.current) {
        processMouseMove(e);
        return;
      }

      pendingMoveRef.current = e;

      if (rafIdRef.current === null) {
        rafIdRef.current = requestAnimationFrame(() => {
          rafIdRef.current = null;
          const pending = pendingMoveRef.current;
          if (pending) {
            pendingMoveRef.current = null;
            processMouseMove(pending);
          }
        });
      }
    },
    [isPanningRef, handlePanMove, processMouseMove],
  );

  // Cancel pending RAF on unmount
  useEffect(() => {
    return () => {
      if (rafIdRef.current !== null) {
        cancelAnimationFrame(rafIdRef.current);
      }
    };
  }, []);

  // ── Mouse Up ──
  const handleMouseUp = useCallback(
    (e: Konva.KonvaEventObject<MouseEvent>) => {
      handlePanEnd();

      // Finish item drag
      if (isDraggingItemRef.current) {
        isDraggingItemRef.current = false;
        dragItemStartRef.current = null;
      }

      // Finish drag select
      if (isDraggingSelectRef.current && dragStartRef.current) {
        const worldPoint = getWorldPoint(e);
        const rect = selectionRect(dragStartRef.current, worldPoint);

        // Only if the rectangle is non-trivial
        if (rect.width > 0.01 || rect.height > 0.01) {
          // Skip furniture when globally transparent — see hit-test above.
          const selectableFurniture = globalFurnitureOpacity < 0.05 ? [] : furnitureItems;
          const ids = elementsInRect(rect, openings, walls, electricalItems, selectableFurniture);
          if (ids.size > 0) {
            selectionDispatch({ type: 'SET_SELECTED', ids });
          }
        }

        dragStartRef.current = null;
        isDraggingSelectRef.current = false;
        setDragRect(null);
      }

      // Finish measurement
      if (isMeasuringRef.current && measureState) {
        const worldPoint = getWorldPoint(e);
        const result = finishMeasurement(updateMeasurement(measureState, worldPoint));
        // Keep the measurement visible (don't clear it)
        setMeasureState(result);
        isMeasuringRef.current = false;
      }
    },
    [handlePanEnd, getWorldPoint, openings, walls, electricalItems, furnitureItems, selectionDispatch, measureState],
  );

  // ── Prevent context menu on canvas ──
  useEffect(() => {
    const stage = stageRef.current;
    if (!stage) return;
    const container = stage.container();
    const handler = (e: Event) => e.preventDefault();
    container.addEventListener('contextmenu', handler);
    return () => container.removeEventListener('contextmenu', handler);
  }, []);

  const cursor = TOOL_CURSORS[activeTool] ?? 'default';

  return (
    <div style={{ position: 'relative', width, height }}>
    <Stage
      ref={stageRef}
      width={width}
      height={height}
      onWheel={handleWheel}
      onMouseDown={handleMouseDown}
      onMouseMove={handleMouseMove}
      onMouseUp={handleMouseUp}
      style={{ cursor, background: canvasColors.canvasBg }}
    >
      {/*
        Konva renders one HTML <canvas> per <Layer>; performance recommends 3-5
        layers max. The previous tree had 10 Layers — one per logical group —
        which fired runtime warnings on Stage. We now collapse them into 3
        actual canvases (background, content, overlay) and use Group internally
        for each logical "layer". Visibility/listening props are preserved on
        the Group roots.
      */}

      {/* Background canvas — grid + rulers (rarely interacted with) */}
      <Layer listening={false}>
        <GridLayer
          zoom={zoom}
          panOffset={panOffset}
          stageWidth={width}
          stageHeight={height}
          gridSize={gridSize}
          visible={gridVisible}
        />
      </Layer>

      {/* Content canvas — room geometry, items, annotations, measurements */}
      <Layer>
        {layerVisibility.walls && (
          <WallLayer
            walls={walls}
            roomShape={room.shape}
            zoom={zoom}
            panOffset={panOffset}
            selectedIds={selectedIds}
            plinthThickness={room.plinthThickness}
          />
        )}

        <OpeningLayer
          openings={openings}
          walls={walls}
          roomShape={room.shape}
          zoom={zoom}
          panOffset={panOffset}
          selectedIds={selectedIds}
          preview={openingPreview}
        />

        <ElectricalLayer
          items={electricalItems}
          zoom={zoom}
          panOffset={panOffset}
          selectedIds={selectedIds}
          visible={layerVisibility.electrical}
          outletWidth={room.outletWidth}
          outletHeight={room.outletHeight}
        />

        <FurnitureLayer
          items={furnitureItems}
          zoom={zoom}
          panOffset={panOffset}
          selectedIds={selectedIds}
          visible={layerVisibility.furniture}
          globalOpacity={globalFurnitureOpacity}
        />

        {layerVisibility.measurements && (
          <MeasurementLayer
            walls={walls}
            openings={openings}
            zoom={zoom}
            panOffset={panOffset}
            selectedIds={selectedIds}
            roomShape={room.shape}
          />
        )}

        {layerVisibility.measurements && (
          <RoomLabelLayer
            roomName={room.name}
            roomShape={room.shape}
            zoom={zoom}
            panOffset={panOffset}
          />
        )}

        {layerVisibility.annotations && (
          <AnnotationLayer
            annotations={annotations}
            electricalItems={electricalItems}
            furnitureItems={furnitureItems}
            zoom={zoom}
            panOffset={panOffset}
            selectedIds={selectedIds}
            onSelect={(id) => selectElement(id)}
            onDragEnd={(id, x, y) => {
              const ann = annotations.find((a) => a.id === id);
              if (ann) updateAnnotation({ ...ann, x, y });
            }}
            onDoubleClick={(id) => setEditingAnnotationId(id)}
          />
        )}
      </Layer>

      {/* Overlay canvas — transient interaction feedback (measure tool, selection) */}
      <Layer listening={false}>
        <MeasureOverlayLayer
          measurement={measureState}
          zoom={zoom}
          panOffset={panOffset}
        />
        <SelectionLayer
          zoom={zoom}
          panOffset={panOffset}
          selectionBox={selBox}
          dragRect={dragRect}
        />
      </Layer>
    </Stage>
    <ScaleBar zoom={zoom} />
    <TextPromptModal
      open={editingAnnotation != null}
      title={t('annotation.editPrompt')}
      initialValue={editingAnnotation?.text ?? ''}
      onConfirm={(value) => {
        if (editingAnnotation && value !== editingAnnotation.text) {
          updateAnnotation({ ...editingAnnotation, text: value });
        }
        setEditingAnnotationId(null);
      }}
      onCancel={() => setEditingAnnotationId(null)}
    />
    </div>
  );
}
