/**
 * @ai-summary Hook for managing image cropping state and operations
 * @ai-context Handles crop area manipulation, touch/mouse interactions, and crop execution
 */

import { useState, useCallback, useRef, useEffect } from 'react';
import type { CropArea } from './types';

interface UseImageCropOptions {
  /** Initial crop area as percentage of image */
  initialCrop?: CropArea;
  /** Lock aspect ratio (width/height) */
  aspectRatio?: number;
  /** Minimum crop size as percentage */
  minSize?: number;
}

interface UseImageCropReturn {
  /** Current crop area */
  cropArea: CropArea;
  /** Whether user has drawn a crop rectangle */
  cropDrawn: boolean;
  /** Whether user is actively dragging */
  isDragging: boolean;
  /** Set crop area */
  setCropArea: (area: CropArea) => void;
  /** Reset crop to drawing mode */
  resetCrop: () => void;
  /** Execute crop and return cropped blob */
  executeCrop: (imageSrc: string, mimeType?: string) => Promise<Blob>;
  /** Handle drag start for crop handles */
  handleDragStart: (handle: CropHandle, event: React.MouseEvent | React.TouchEvent) => void;
  /** Handle draw start for free-form rectangle drawing */
  handleDrawStart: (event: React.MouseEvent | React.TouchEvent) => void;
  /** Handle move during drag */
  handleMove: (event: MouseEvent | TouchEvent) => void;
  /** Handle drag end */
  handleDragEnd: () => void;
}

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

/**
 * Hook for managing image cropping
 */
export function useImageCrop(options: UseImageCropOptions = {}): UseImageCropReturn {
  const { aspectRatio, minSize = 10 } = options;

  const defaultCrop: CropArea = {
    x: 10,
    y: 10,
    width: 80,
    height: 80,
  };

  const initialCrop = options.initialCrop || defaultCrop;

  // Apply aspect ratio to initial crop if needed
  const getAspectRatioAdjustedCrop = useCallback(
    (crop: CropArea): CropArea => {
      if (!aspectRatio) return crop;

      // Adjust height based on width and aspect ratio
      const newHeight = crop.width / aspectRatio;
      return {
        ...crop,
        height: Math.min(newHeight, 100 - crop.y),
      };
    },
    [aspectRatio]
  );

  const [cropArea, setCropAreaState] = useState<CropArea>(
    getAspectRatioAdjustedCrop(initialCrop)
  );
  const [cropDrawn, setCropDrawn] = useState(false);
  const [isDragging, setIsDragging] = useState(false);

  const activeHandleRef = useRef<CropHandle | null>(null);
  const startPosRef = useRef({ x: 0, y: 0 });
  const startCropRef = useRef<CropArea>(cropArea);
  const containerRef = useRef<{ width: number; height: number; left: number; top: number }>({
    width: 100, height: 100, left: 0, top: 0,
  });
  const isDrawingRef = useRef(false);
  const drawOriginRef = useRef({ x: 0, y: 0 });
  const cropAreaRef = useRef(cropArea);

  const setCropArea = useCallback(
    (area: CropArea) => {
      setCropAreaState(getAspectRatioAdjustedCrop(area));
    },
    [getAspectRatioAdjustedCrop]
  );

  const resetCrop = useCallback(() => {
    setCropAreaState(getAspectRatioAdjustedCrop(initialCrop));
    setCropDrawn(false);
  }, [initialCrop, getAspectRatioAdjustedCrop]);

  const constrainCrop = useCallback(
    (crop: CropArea): CropArea => {
      let { x, y, width, height } = crop;

      // Ensure minimum size
      width = Math.max(width, minSize);
      height = Math.max(height, minSize);

      // Apply aspect ratio if set
      if (aspectRatio) {
        height = width / aspectRatio;
      }

      // Constrain to bounds
      x = Math.max(0, Math.min(x, 100 - width));
      y = Math.max(0, Math.min(y, 100 - height));
      width = Math.min(width, 100 - x);
      height = Math.min(height, 100 - y);

      return { x, y, width, height };
    },
    [aspectRatio, minSize]
  );

  const handleDragStart = useCallback(
    (handle: CropHandle, event: React.MouseEvent | React.TouchEvent) => {
      event.preventDefault();
      activeHandleRef.current = handle;
      startCropRef.current = { ...cropArea };
      setIsDragging(true);

      const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
      const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
      startPosRef.current = { x: clientX, y: clientY };

      // Get container dimensions from the event target's parent
      const target = event.currentTarget as HTMLElement;
      const container = target.closest('[data-crop-container]');
      if (container) {
        const rect = container.getBoundingClientRect();
        containerRef.current = { width: rect.width, height: rect.height, left: rect.left, top: rect.top };
      }
    },
    [cropArea]
  );

  const handleDrawStart = useCallback(
    (event: React.MouseEvent | React.TouchEvent) => {
      event.preventDefault();

      const target = event.currentTarget as HTMLElement;
      const container = target.closest('[data-crop-container]');
      if (!container) return;

      const rect = container.getBoundingClientRect();
      containerRef.current = { width: rect.width, height: rect.height, left: rect.left, top: rect.top };

      const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
      const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;

      const x = Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100));
      const y = Math.max(0, Math.min(100, ((clientY - rect.top) / rect.height) * 100));

      startPosRef.current = { x: clientX, y: clientY };
      drawOriginRef.current = { x, y };

      const initial = { x, y, width: 0, height: 0 };
      setCropAreaState(initial);
      cropAreaRef.current = initial;

      isDrawingRef.current = true;
      activeHandleRef.current = null;
      setIsDragging(true);
    },
    []
  );

  const handleMove = useCallback(
    (event: MouseEvent | TouchEvent) => {
      if (!isDragging) return;

      const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
      const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;

      // Free-form drawing mode: compute rectangle from origin to current pointer
      if (isDrawingRef.current) {
        const currentX = Math.max(0, Math.min(100,
          ((clientX - containerRef.current.left) / containerRef.current.width) * 100));
        const currentY = Math.max(0, Math.min(100,
          ((clientY - containerRef.current.top) / containerRef.current.height) * 100));

        const originX = drawOriginRef.current.x;
        const originY = drawOriginRef.current.y;

        const drawnWidth = Math.abs(currentX - originX);
        const drawnHeight = aspectRatio
          ? drawnWidth / aspectRatio
          : Math.abs(currentY - originY);
        let drawnY = Math.min(originY, currentY);
        // Clamp so crop doesn't exceed container bounds when aspect ratio forces height
        if (aspectRatio && drawnY + drawnHeight > 100) {
          drawnY = Math.max(0, 100 - drawnHeight);
        }
        const newCrop: CropArea = {
          x: Math.min(originX, currentX),
          y: drawnY,
          width: drawnWidth,
          height: drawnHeight,
        };

        setCropAreaState(newCrop);
        cropAreaRef.current = newCrop;
        return;
      }

      if (!activeHandleRef.current) return;

      // Calculate delta as percentage of container
      const deltaX = ((clientX - startPosRef.current.x) / containerRef.current.width) * 100;
      const deltaY = ((clientY - startPosRef.current.y) / containerRef.current.height) * 100;

      const start = startCropRef.current;
      let newCrop: CropArea = { ...start };

      switch (activeHandleRef.current) {
        case 'move':
          newCrop = {
            ...start,
            x: start.x + deltaX,
            y: start.y + deltaY,
          };
          break;

        case 'nw':
          newCrop = {
            x: start.x + deltaX,
            y: start.y + deltaY,
            width: start.width - deltaX,
            height: start.height - deltaY,
          };
          break;

        case 'n':
          newCrop = {
            ...start,
            y: start.y + deltaY,
            height: start.height - deltaY,
          };
          break;

        case 'ne':
          newCrop = {
            ...start,
            y: start.y + deltaY,
            width: start.width + deltaX,
            height: start.height - deltaY,
          };
          break;

        case 'e':
          newCrop = {
            ...start,
            width: start.width + deltaX,
          };
          break;

        case 'se':
          newCrop = {
            ...start,
            width: start.width + deltaX,
            height: start.height + deltaY,
          };
          break;

        case 's':
          newCrop = {
            ...start,
            height: start.height + deltaY,
          };
          break;

        case 'sw':
          newCrop = {
            x: start.x + deltaX,
            y: start.y,
            width: start.width - deltaX,
            height: start.height + deltaY,
          };
          break;

        case 'w':
          newCrop = {
            x: start.x + deltaX,
            y: start.y,
            width: start.width - deltaX,
            height: start.height,
          };
          break;
      }

      const constrained = constrainCrop(newCrop);
      setCropAreaState(constrained);
      cropAreaRef.current = constrained;
    },
    [isDragging, constrainCrop, aspectRatio]
  );

  const handleDragEnd = useCallback(() => {
    if (isDrawingRef.current) {
      isDrawingRef.current = false;
      const area = cropAreaRef.current;
      // When aspect ratio constrains one dimension, only check the free dimension
      const meetsMinSize = aspectRatio
        ? area.width >= minSize
        : area.width >= minSize && area.height >= minSize;
      if (meetsMinSize) {
        setCropDrawn(true);
      }
    }
    activeHandleRef.current = null;
    setIsDragging(false);
  }, [minSize, aspectRatio]);

  // Add global event listeners for drag
  useEffect(() => {
    if (isDragging) {
      window.addEventListener('mousemove', handleMove);
      window.addEventListener('mouseup', handleDragEnd);
      window.addEventListener('touchmove', handleMove, { passive: false });
      window.addEventListener('touchend', handleDragEnd);
    }

    return () => {
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', handleDragEnd);
      window.removeEventListener('touchmove', handleMove);
      window.removeEventListener('touchend', handleDragEnd);
    };
  }, [isDragging, handleMove, handleDragEnd]);

  const executeCrop = useCallback(
    async (imageSrc: string, mimeType: string = 'image/jpeg'): Promise<Blob> => {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'anonymous';

        img.onload = () => {
          const canvas = document.createElement('canvas');
          const ctx = canvas.getContext('2d');

          if (!ctx) {
            reject(new Error('Failed to get canvas context'));
            return;
          }

          // Calculate actual pixel values from percentages
          const cropX = (cropArea.x / 100) * img.width;
          const cropY = (cropArea.y / 100) * img.height;
          const cropWidth = (cropArea.width / 100) * img.width;
          const cropHeight = (cropArea.height / 100) * img.height;

          canvas.width = cropWidth;
          canvas.height = cropHeight;

          ctx.drawImage(
            img,
            cropX,
            cropY,
            cropWidth,
            cropHeight,
            0,
            0,
            cropWidth,
            cropHeight
          );

          canvas.toBlob(
            (blob) => {
              if (blob) {
                resolve(blob);
              } else {
                reject(new Error('Failed to create blob from canvas'));
              }
            },
            mimeType,
            0.95
          );
        };

        img.onerror = () => {
          reject(new Error('Failed to load image for cropping'));
        };

        img.src = imageSrc;
      });
    },
    [cropArea]
  );

  return {
    cropArea,
    cropDrawn,
    isDragging,
    setCropArea,
    resetCrop,
    executeCrop,
    handleDragStart,
    handleDrawStart,
    handleMove,
    handleDragEnd,
  };
}

export default useImageCrop;
