'use client';

import { useEffect, useRef, useState, useCallback } from 'react';
import { useCanvasStore, useLayerStore, useToolStore } from '@/store';
import { useHistoryStore } from '@/store/history-store';
import { useSelectionStore } from '@/store/selection-store';
import { useTextStore } from '@/store/text-store';
import { drawMarchingAnts } from '@/lib/selection-utils';
import { getContext, drawGrid, drawCheckerboard } from '@/lib/canvas-utils';
import { renderText } from '@/lib/text-utils';
import { DrawCommand } from '@/core/commands';
import { getTool, preloadCommonTools } from '@/lib/tool-loader';
import { useTouchGestures } from '@/hooks/use-touch-gestures';
import type { BaseTool } from '@/tools';
import type { PointerState } from '@/types';
import { cn } from '@/lib/utils';
import { OnCanvasTextEditor } from './on-canvas-text-editor';

export function CanvasWithTools() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const drawCommandRef = useRef<DrawCommand | null>(null);
  const toolsCache = useRef<Record<string, BaseTool>>({});

  const {
    width,
    height,
    zoom,
    offsetX,
    offsetY,
    showGrid,
    gridSize,
    backgroundColor,
    selection,
    screenToCanvas,
  } = useCanvasStore();

  const { layers, getActiveLayer } = useLayerStore();
  const { activeTool, settings } = useToolStore();
  const { executeCommand } = useHistoryStore();
  const { activeSelection, selectionType, isMarching } = useSelectionStore();
  const { textObjects, editingTextId, isOnCanvasEditorActive } = useTextStore();
  const [marchingOffset, setMarchingOffset] = useState(0);

  const [isPanning, setIsPanning] = useState(false);
  const [panStart, setPanStart] = useState({ x: 0, y: 0 });
  const [pointer, setPointer] = useState<PointerState>({
    isDown: false,
    x: 0,
    y: 0,
    prevX: 0,
    prevY: 0,
    pressure: 1,
  });

  // Touch gesture support for mobile
  useTouchGestures(containerRef, {
    minScale: 0.1,
    maxScale: 32,
  });

  // Helper to get tool (lazy load if not in cache)
  const getToolInstance = useCallback(async (toolKey: string): Promise<BaseTool | null> => {
    if (toolsCache.current[toolKey]) {
      return toolsCache.current[toolKey];
    }

    try {
      const tool = await getTool(toolKey as any);
      toolsCache.current[toolKey] = tool;
      return tool;
    } catch (error) {
      console.error(`Failed to load tool ${toolKey}:`, error);
      return null;
    }
  }, []);

  // Preload common tools on mount
  useEffect(() => {
    preloadCommonTools();

    // Preload tools into cache
    const initTools = async () => {
      const commonTools = ['pencil', 'brush', 'eraser'];
      for (const toolKey of commonTools) {
        await getToolInstance(toolKey);
      }
    };

    initTools();
  }, [getToolInstance]);

  // Eagerly load active tool when it changes
  useEffect(() => {
    const loadActiveTool = async () => {
      // Load the active tool
      await getToolInstance(activeTool);

      // For selection tools, also load the specific selection type
      if (activeTool === 'select') {
        await getToolInstance(`${selectionType}-select`);
      }
    };

    loadActiveTool();
  }, [activeTool, selectionType, getToolInstance]);

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

    const ctx = getContext(canvas);
    const container = containerRef.current;
    if (!container) return;

    // Set canvas size to match container
    const rect = container.getBoundingClientRect();
    canvas.width = rect.width;
    canvas.height = rect.height;

    // Clear canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Save context state
    ctx.save();

    // Apply transformations
    ctx.translate(offsetX + canvas.width / 2, offsetY + canvas.height / 2);
    ctx.scale(zoom, zoom);
    ctx.translate(-width / 2, -height / 2);

    // Draw checkerboard background (only within canvas bounds)
    drawCheckerboard(ctx, 10, '#ffffff', '#e0e0e0', width, height);

    // Draw background color if not transparent
    if (backgroundColor && backgroundColor !== 'transparent') {
      ctx.fillStyle = backgroundColor;
      ctx.fillRect(0, 0, width, height);
    }

    // Draw all visible layers
    layers
      .filter((layer) => layer.visible && layer.canvas)
      .sort((a, b) => a.order - b.order)
      .forEach((layer) => {
        if (!layer.canvas) return;

        ctx.globalAlpha = layer.opacity;
        ctx.globalCompositeOperation = layer.blendMode as GlobalCompositeOperation;
        ctx.drawImage(layer.canvas, layer.x, layer.y);
      });

    // Reset composite operation
    ctx.globalAlpha = 1;
    ctx.globalCompositeOperation = 'source-over';

    // Draw text objects (skip the one being edited)
    textObjects.forEach((textObj) => {
      // Don't render text that's currently being edited
      if (editingTextId && textObj.id === editingTextId) {
        return;
      }

      renderText(ctx, textObj.x, textObj.y, {
        text: textObj.text,
        fontFamily: textObj.fontFamily,
        fontSize: textObj.fontSize,
        fontStyle: textObj.fontStyle,
        fontWeight: textObj.fontWeight,
        color: textObj.color,
        align: textObj.align,
        baseline: textObj.baseline,
        lineHeight: textObj.lineHeight,
        letterSpacing: textObj.letterSpacing,
      });
    });

    // Draw grid if enabled (only within canvas bounds)
    if (showGrid) {
      drawGrid(ctx, gridSize, 'rgba(0, 0, 0, 0.15)', width, height);
    }

    // Draw selection if active (marching ants)
    if (activeSelection && isMarching) {
      drawMarchingAnts(ctx, activeSelection.mask, marchingOffset);
    }

    // Restore context state
    ctx.restore();
  }, [layers, width, height, zoom, offsetX, offsetY, showGrid, gridSize, backgroundColor, selection, pointer, activeSelection, isMarching, marchingOffset, textObjects, editingTextId]);

  // Marching ants animation
  useEffect(() => {
    if (!activeSelection || !isMarching) return;

    const interval = setInterval(() => {
      setMarchingOffset((prev) => (prev + 1) % 8);
    }, 50);

    return () => clearInterval(interval);
  }, [activeSelection, isMarching]);

  // Handle mouse wheel for zooming
  const handleWheel = (e: React.WheelEvent) => {
    if (e.ctrlKey || e.metaKey) {
      e.preventDefault();
      const { zoomIn, zoomOut } = useCanvasStore.getState();
      if (e.deltaY < 0) {
        zoomIn();
      } else {
        zoomOut();
      }
    }
  };

  // Handle pointer down
  const handlePointerDown = (e: React.PointerEvent) => {
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;

    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;
    const canvasPos = screenToCanvas(screenX, screenY, rect.width, rect.height);

    // Check for panning
    if (e.button === 1 || (e.button === 0 && e.shiftKey)) {
      setIsPanning(true);
      setPanStart({ x: e.clientX - offsetX, y: e.clientY - offsetY });
      e.preventDefault();
      return;
    }

    // Transform tools
    const transformTools = ['move', 'transform'];
    if (e.button === 0 && !e.shiftKey && transformTools.includes(activeTool)) {
      const tool = toolsCache.current[activeTool];
      if (!tool) return; // Tool not loaded yet

      const newPointer: PointerState = {
        isDown: true,
        x: canvasPos.x,
        y: canvasPos.y,
        prevX: canvasPos.x,
        prevY: canvasPos.y,
        pressure: e.pressure || 1,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        metaKey: e.metaKey,
      };

      setPointer(newPointer);
      tool.onPointerDown(newPointer, {} as any, settings);
      return;
    }

    // Text tool - only handle if editor is not already active
    if (activeTool === 'text') {
      // If editor is active, let it handle its own events (selection, dragging, click-outside)
      if (isOnCanvasEditorActive) return;

      const activeLayer = getActiveLayer();
      if (!activeLayer || !activeLayer.canvas || activeLayer.locked) return;

      const newPointer: PointerState = {
        isDown: true,
        x: canvasPos.x,
        y: canvasPos.y,
        prevX: canvasPos.x,
        prevY: canvasPos.y,
        pressure: e.pressure || 1,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        metaKey: e.metaKey,
      };

      const textTool = toolsCache.current['text'];
      if (textTool) {
        textTool.onPointerDown(newPointer, {} as any, settings);
      }
      return;
    }

    // Selection tools
    const selectionTools = ['select', 'rectangular-select', 'elliptical-select', 'lasso-select', 'magic-wand'];
    if (e.button === 0 && !e.shiftKey && selectionTools.includes(activeTool)) {
      const activeLayer = getActiveLayer();
      if (!activeLayer || !activeLayer.canvas) return;

      const tool = toolsCache.current[`${selectionType}-select`] || toolsCache.current['select'];
      if (!tool) return; // Tool not loaded yet
      const newPointer: PointerState = {
        isDown: true,
        x: canvasPos.x,
        y: canvasPos.y,
        prevX: canvasPos.x,
        prevY: canvasPos.y,
        pressure: e.pressure || 1,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        metaKey: e.metaKey,
      };

      setPointer(newPointer);

      const ctx = activeLayer.canvas.getContext('2d');
      if (ctx) {
        tool.onPointerDown(newPointer, ctx, settings);
      }
      return;
    }

    // Drawing tools
    if (e.button === 0 && !e.shiftKey && ['pencil', 'brush', 'eraser', 'fill', 'eyedropper', 'shape', 'clone', 'smudge', 'dodge'].includes(activeTool)) {
      const activeLayer = getActiveLayer();
      if (!activeLayer || !activeLayer.canvas || activeLayer.locked) return;

      const newPointer: PointerState = {
        isDown: true,
        x: canvasPos.x,
        y: canvasPos.y,
        prevX: canvasPos.x,
        prevY: canvasPos.y,
        pressure: e.pressure || 1,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        metaKey: e.metaKey,
      };

      setPointer(newPointer);

      const tool = toolsCache.current[activeTool];
      if (!tool) return; // Tool not loaded yet

      // Create draw command for history
      drawCommandRef.current = new DrawCommand(activeLayer.id, tool.name);

      // Call tool's onPointerDown
      const ctx = activeLayer.canvas.getContext('2d');
      if (ctx) {
        tool.onPointerDown(newPointer, ctx, settings);
      }
    }
  };

  // Handle pointer move
  const handlePointerMove = (e: React.PointerEvent) => {
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;

    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;
    const canvasPos = screenToCanvas(screenX, screenY, rect.width, rect.height);

    // Panning
    if (isPanning) {
      const { setPanOffset } = useCanvasStore.getState();
      setPanOffset(e.clientX - panStart.x, e.clientY - panStart.y);
      return;
    }

    // Drawing
    if (pointer.isDown && ['pencil', 'brush', 'eraser', 'eyedropper', 'shape', 'clone', 'smudge', 'dodge'].includes(activeTool)) {
      const activeLayer = getActiveLayer();
      if (!activeLayer || !activeLayer.canvas) return;

      const newPointer: PointerState = {
        ...pointer,
        x: canvasPos.x,
        y: canvasPos.y,
        pressure: e.pressure || 1,
        altKey: e.altKey,
        ctrlKey: e.ctrlKey,
        shiftKey: e.shiftKey,
        metaKey: e.metaKey,
      };

      setPointer(newPointer);

      const tool = toolsCache.current[activeTool];
      if (!tool) return; // Tool not loaded yet

      const ctx = activeLayer.canvas.getContext('2d');
      if (ctx) {
        tool.onPointerMove(newPointer, ctx, settings);
      }
    }
  };

  // Handle pointer up
  const handlePointerUp = (e: React.PointerEvent) => {
    if (isPanning) {
      setIsPanning(false);
      return;
    }

    if (pointer.isDown && ['pencil', 'brush', 'eraser', 'fill', 'eyedropper', 'shape'].includes(activeTool)) {
      const activeLayer = getActiveLayer();
      if (!activeLayer || !activeLayer.canvas) return;

      const tool = toolsCache.current[activeTool];
      if (!tool) return; // Tool not loaded yet

      const ctx = activeLayer.canvas.getContext('2d');
      if (ctx) {
        tool.onPointerUp(pointer, ctx, settings);
      }

      // Capture after state and add to history
      if (drawCommandRef.current) {
        drawCommandRef.current.captureAfterState();
        executeCommand(drawCommandRef.current);
        drawCommandRef.current = null;
      }

      setPointer({ ...pointer, isDown: false });
    }
  };

  return (
    <div
      ref={containerRef}
      className={cn(
        'relative h-full w-full overflow-hidden bg-canvas-bg touch-none',
        isPanning ? 'cursor-grabbing' : `cursor-${toolsCache.current[activeTool]?.getCursor(settings) || 'default'}`
      )}
      style={{ touchAction: 'none' }}
      onWheel={handleWheel}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerLeave={handlePointerUp}
    >
      <canvas
        ref={canvasRef}
        className="absolute inset-0"
      />

      {/* On-canvas text editor */}
      <OnCanvasTextEditor />
    </div>
  );
}
