import { useState, useEffect, MouseEvent, useRef, Dispatch, SetStateAction } from 'react';

type Coordinates = {
  x: number;
  y: number;
}

interface DraggableBoxProps {
  position: Coordinates;
  className?: string;
  children?: React.ReactNode;
  coordSetter: Dispatch<SetStateAction<Coordinates>>;
  mode?: 'handle' | 'box';
  width?: number;
  height?: number;
  onDrag?: (delta: Coordinates) => void;
}

const DraggableBox = ({
  position,
  className = "",
  children = "",
  coordSetter,
  mode = 'handle',
  width = 0,
  height = 0,
  onDrag
}: DraggableBoxProps) => {
  const [isDragging, setIsDragging] = useState(false);
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
  const dragRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!isDragging) return;

    const handleMouseMove = (e: globalThis.MouseEvent) => {
      if (!dragRef.current?.parentElement) return;

      const parent = dragRef.current.parentElement;
      const parentRect = parent.getBoundingClientRect();

      if (mode === 'handle') {
        const x = ((e.clientX - parentRect.left - dragOffset.x) / parentRect.width) * 100;
        const y = ((e.clientY - parentRect.top - dragOffset.y) / parentRect.height) * 100;

        const newX = Math.min(Math.max(0, x), 100);
        const newY = Math.min(Math.max(0, y), 100);

        coordSetter({
          x: Number(newX.toFixed(1)),
          y: Number(newY.toFixed(1))
        });
      } else {
        const x = ((e.clientX - parentRect.left - dragOffset.x) / parentRect.width) * 100;
        const y = ((e.clientY - parentRect.top - dragOffset.y) / parentRect.height) * 100;

        const maxX = 100 - width;
        const maxY = 100 - height;

        const newX = Math.min(Math.max(0, x), maxX);
        const newY = Math.min(Math.max(0, y), maxY);

        coordSetter({
          x: Number(newX.toFixed(1)),
          y: Number(newY.toFixed(1))
        });

        onDrag?.({ x: newX, y: newY });
      }
    };

    const handleMouseUp = () => setIsDragging(false);

    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);

    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, dragOffset, mode, width, height, onDrag]);

  const handleMouseDown = (e: MouseEvent) => {
    if (!dragRef.current?.parentElement) return;

    const parentRect = dragRef.current.parentElement.getBoundingClientRect();
    const elementRect = dragRef.current.getBoundingClientRect();

    if (mode === 'handle') {
      setDragOffset({
        x: e.clientX - elementRect.left,
        y: e.clientY - elementRect.top
      });
    } else {
      setDragOffset({
        x: e.clientX - parentRect.left - (position.x * parentRect.width / 100),
        y: e.clientY - parentRect.top - (position.y * parentRect.height / 100)
      });
    }

    setIsDragging(true);
  };

  const style = mode === 'handle' ? {
    left: `${position.x}%`,
    top: `${position.y}%`,
    transform: 'translate(-50%, -50%)', // Center the handle
  } : {
    left: `${position.x}%`,
    top: `${position.y}%`,
    width: `${width}%`,
    height: `${height}%`,
  };

  return (
    <div
      ref={dragRef}
      className={`absolute cursor-pointer transform-gpu ${mode === 'box' ? 'cursor-move' : ''} ${className}`}
      style={{
        ...style,
        userSelect: 'none',
      }}
      onMouseDown={handleMouseDown}
    >
      {children}
    </div>
  );
};

export default DraggableBox;
