// ─────────────────────────────────────────────────────────────────────────────
// Ultra Image Cropper 2.0 — Crop Overlay
// Interactive drag handles rendered on top of canvas
// ─────────────────────────────────────────────────────────────────────────────

import React, { useRef, useCallback, useEffect, useMemo } from 'react';
import { useCanvasStore } from '@/store';
import { ratioToNumber } from '@/utils/contextPresets';

interface CropOverlayProps {
  canvasWidth: number;
  canvasHeight: number;
  minWidth: number;
}

type HandlePosition = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'move';

export const CropOverlay: React.FC<CropOverlayProps> = ({ canvasWidth, canvasHeight, minWidth }) => {
  const { crop, setCrop, canvasWidth: actualCanvasWidth, canvasHeight: actualCanvasHeight, zoom } = useCanvasStore();
  const svgRef = useRef<SVGSVGElement>(null);
  const dragState = useRef<{
    handle: HandlePosition;
    startX: number;
    startY: number;
    startCrop: typeof crop;
  } | null>(null);

  // Initialize crop to full canvas if not set
  useEffect(() => {
    if (crop.width === 0 && crop.height === 0 && actualCanvasWidth > 0) {
      setCrop({ x: 0, y: 0, width: actualCanvasWidth, height: actualCanvasHeight });
    }
  }, [actualCanvasWidth, actualCanvasHeight, crop.width, crop.height, setCrop]);

  // Enforce aspect ratio whenever the ratio prop changes (set by CropPanel button click)
  const prevRatioRef = useRef(crop.ratio);
  useEffect(() => {
    if (crop.ratio === prevRatioRef.current) return;
    prevRatioRef.current = crop.ratio;
    const ar = ratioToNumber(crop.ratio);
    if (!ar || crop.width === 0) return;
    const newH = Math.round(crop.width / ar);
    const clampedH = Math.min(newH, canvasHeight - crop.y);
    const newW = Math.round(clampedH * ar);
    setCrop({ width: newW, height: clampedH });
  }, [crop.ratio, crop.width, crop.y, canvasHeight, setCrop]);

  const { x, y, width, height, ratio } = crop;
  const aspectRatio = ratioToNumber(ratio);

  const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));

  const handleMouseDown = useCallback((e: React.MouseEvent, handle: HandlePosition) => {
    e.preventDefault();
    e.stopPropagation();
    const svgRect = svgRef.current!.getBoundingClientRect();
    dragState.current = {
      handle,
      startX: e.clientX - svgRect.left,
      startY: e.clientY - svgRect.top,
      startCrop: { ...crop },
    };
  }, [crop]);

  const handleMouseMove = useCallback((e: MouseEvent) => {
    if (!dragState.current || !svgRef.current) return;
    const svgRect = svgRef.current.getBoundingClientRect();
    const mx = e.clientX - svgRect.left;
    const my = e.clientY - svgRect.top;
    const { handle, startX, startY, startCrop } = dragState.current;
    const dx = (mx - startX) / zoom;
    const dy = (my - startY) / zoom;

    let newX = startCrop.x;
    let newY = startCrop.y;
    let newW = startCrop.width;
    let newH = startCrop.height;

    if (handle === 'move') {
      newX = clamp(startCrop.x + dx, 0, actualCanvasWidth - newW);
      newY = clamp(startCrop.y + dy, 0, actualCanvasHeight - newH);
    } else {
      if (handle.includes('e')) newW = clamp(startCrop.width + dx, (minWidth || 20) / zoom, actualCanvasWidth - startCrop.x);
      if (handle.includes('s')) newH = clamp(startCrop.height + dy, 20 / zoom, actualCanvasHeight - startCrop.y);
      if (handle.includes('w')) {
        const maxDx = startCrop.width - (minWidth || 20) / zoom;
        const actualDx = clamp(dx, -startCrop.x, maxDx);
        newX = startCrop.x + actualDx;
        newW = startCrop.width - actualDx;
      }
      if (handle.includes('n')) {
        const maxDy = startCrop.height - 20 / zoom;
        const actualDy = clamp(dy, -startCrop.y, maxDy);
        newY = startCrop.y + actualDy;
        newH = startCrop.height - actualDy;
      }
    }

    // Enforce aspect ratio
    if (aspectRatio && handle !== 'move') {
      if (['e', 'w'].includes(handle)) {
        newH = newW / aspectRatio;
      } else if (['n', 's'].includes(handle)) {
        newW = newH * aspectRatio;
      } else {
        newH = newW / aspectRatio;
      }
    }

    setCrop({ x: Math.round(newX), y: Math.round(newY), width: Math.round(newW), height: Math.round(newH) });
  }, [actualCanvasWidth, actualCanvasHeight, minWidth, aspectRatio, setCrop, zoom]);

  const handleMouseUp = useCallback(() => {
    dragState.current = null;
  }, []);

  useEffect(() => {
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);
    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [handleMouseMove, handleMouseUp]);

  const HANDLE_SIZE = 10;
  const handles: { pos: HandlePosition; cx: number; cy: number }[] = [
    { pos: 'nw', cx: x * zoom,          cy: y * zoom          },
    { pos: 'n',  cx: x * zoom + width * zoom / 2, cy: y * zoom         },
    { pos: 'ne', cx: x * zoom + width * zoom,  cy: y * zoom          },
    { pos: 'e',  cx: x * zoom + width * zoom,  cy: y * zoom + height * zoom / 2 },
    { pos: 'se', cx: x * zoom + width * zoom,  cy: y * zoom + height * zoom },
    { pos: 's',  cx: x * zoom + width * zoom / 2, cy: y * zoom + height * zoom },
    { pos: 'sw', cx: x * zoom,          cy: y * zoom + height * zoom },
    { pos: 'w',  cx: x * zoom,          cy: y * zoom + height * zoom / 2 },
  ];

  const cursorMap: Record<HandlePosition, string> = {
    nw: 'nw-resize', n: 'n-resize', ne: 'ne-resize',
    e: 'e-resize', se: 'se-resize', s: 's-resize',
    sw: 'sw-resize', w: 'w-resize', move: 'move',
  };

  const shape = crop.shape ?? 'rectangle';
  const cx = x * zoom + width * zoom / 2;
  const cy = y * zoom + height * zoom / 2;
  const circleR = Math.min(width * zoom, height * zoom) / 2;
  const rrRadius = Math.min(width * zoom, height * zoom) * 0.1;

  // Build an evenodd compound path: outer canvas rect + inner crop shape.
  // With fill-rule="evenodd", the clipPath selects the area BETWEEN the two
  // paths — i.e. outside the crop shape — so the dark overlay only renders
  // there and is completely absent inside the crop window. Using clipPath
  // instead of a semi-transparent mask means the overlay is fully opaque over
  // any transparent canvas pixels (no checkerboard bleed-through).
  const buildOutsideClipPath = (): string => {
    const outer = `M0 0 H${canvasWidth} V${canvasHeight} H0 Z`;
    let inner: string;
    switch (shape) {
      case 'circle': {
        const r = circleR;
        inner = `M${cx + r} ${cy} A${r} ${r} 0 1 0 ${cx - r} ${cy} A${r} ${r} 0 1 0 ${cx + r} ${cy} Z`;
        break;
      }
      case 'ellipse': {
        const rx = width * zoom / 2;
        const ry = height * zoom / 2;
        inner = `M${cx + rx} ${cy} A${rx} ${ry} 0 1 0 ${cx - rx} ${cy} A${rx} ${ry} 0 1 0 ${cx + rx} ${cy} Z`;
        break;
      }
      case 'rounded': {
        const r = rrRadius;
        inner = `M${x * zoom + r} ${y * zoom} H${x * zoom + width * zoom - r} A${r} ${r} 0 0 1 ${x * zoom + width * zoom} ${y * zoom + r}`
          + ` V${y * zoom + height * zoom - r} A${r} ${r} 0 0 1 ${x * zoom + width * zoom - r} ${y * zoom + height * zoom}`
          + ` H${x * zoom + r} A${r} ${r} 0 0 1 ${x * zoom} ${y * zoom + height * zoom - r}`
          + ` V${y * zoom + r} A${r} ${r} 0 0 1 ${x * zoom + r} ${y * zoom} Z`;
        break;
      }
      default:
        inner = `M${x * zoom} ${y * zoom} H${x * zoom + width * zoom} V${y * zoom + height * zoom} H${x * zoom} Z`;
    }
    return `${outer} ${inner}`;
  };

  const borderProps = {
    // 'transparent' fill so the entire shape interior captures pointer events —
    // 'none' only fires on the stroke ring, making shapes ungrabbable
    fill: 'transparent' as const,
    stroke: 'white',
    strokeWidth: 1.5,
    onMouseDown: (e: React.MouseEvent) => handleMouseDown(e, 'move'),
    style: { cursor: 'move' as const, pointerEvents: 'all' as const },
  };

  const cropBorder = (() => {
    switch (shape) {
      case 'circle':
        return <circle cx={cx} cy={cy} r={circleR} {...borderProps} />;
      case 'ellipse':
        return <ellipse cx={cx} cy={cy} rx={width * zoom / 2} ry={height * zoom / 2} {...borderProps} />;
      case 'rounded':
        return <rect x={x * zoom} y={y * zoom} width={width * zoom} height={height * zoom} rx={rrRadius} ry={rrRadius} {...borderProps} />;
      default:
        return <rect x={x * zoom} y={y * zoom} width={width * zoom} height={height * zoom} {...borderProps} />;
    }
  })();

  return (
    <svg
      ref={svgRef}
      className="ultra-crop-overlay"
      width={canvasWidth}
      height={canvasHeight}
      style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'all' }}
    >
      <defs>
        <clipPath id="crop-outside-clip">
          <path d={buildOutsideClipPath()} fillRule="evenodd" />
        </clipPath>
      </defs>

      {/* Dark overlay — clipped to only the area outside the crop shape.
          clipPath (not mask) keeps the overlay opaque so transparent canvas
          pixels don't let the page checkerboard bleed through. */}
      <rect x="0" y="0" width={canvasWidth} height={canvasHeight}
        fill="rgba(0,0,0,0.05)" clipPath="url(#crop-outside-clip)" />

      {/* Crop shape border */}
      {cropBorder}

      {/* Corner and edge handles */}
      {handles.map(({ pos, cx: hx, cy: hy }) => (
        <rect
          key={pos}
          x={hx - HANDLE_SIZE / 2}
          y={hy - HANDLE_SIZE / 2}
          width={HANDLE_SIZE}
          height={HANDLE_SIZE}
          fill="white"
          stroke="rgba(0,0,0,0.4)"
          strokeWidth="1"
          style={{ cursor: cursorMap[pos] }}
          onMouseDown={(e) => handleMouseDown(e, pos)}
        />
      ))}
    </svg>
  );
};
