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

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

interface DraggableBoxProps {
  initialX: number;
  initialY: number;
  className?: string;
  children?: React.ReactNode;
  setter?: Dispatch<SetStateAction<Coordinates>>;
}

const DraggableBox = ({
  initialX,
  initialY,
  className = "",
  children = "",
  setter
}: DraggableBoxProps) => {
  const [isDragging, setIsDragging] = useState(false);
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
  const [xPosition, setXPosition] = useState(initialX)
  const [yPosition, setYPosition] = useState(initialY)
  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();

      const relativeX = e.clientX - parentRect.left - dragOffset.x;
      const relativeY = e.clientY - parentRect.top - dragOffset.y;

      const maxX = parentRect.width;
      const maxY = parentRect.height;

      let newX = (Math.max(0, Math.min(relativeX, maxX)));
      let newY = (Math.max(0, Math.min(relativeY, maxY)));

      setXPosition(newX);
      setYPosition(newY);
      if (setter) {
        setter({
          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, xPosition, yPosition, initialX, initialY]);

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

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

    setDragOffset({
      x: e.clientX - parentRect.left - xPosition,
      y: e.clientY - parentRect.top - yPosition
    });
    setIsDragging(true);
  };

  return (
    <div
      ref={dragRef}
      className={`absolute cursor-pointer ${className}`}
      style={{
        transform: `translate(${xPosition}px, ${yPosition}px)`,
        userSelect: 'none',
      }}
      onMouseDown={handleMouseDown}
    >
      {children}
    </div>
  );
};

export default DraggableBox;
