"use client";

import React from 'react';

type SetPos = React.Dispatch<React.SetStateAction<{ x: number; y: number }>>;

export interface DraggableOptions {
  constrainToViewport?: boolean;
  disableIframeDuringDrag?: boolean;
}

export function useDraggableModal(
  modalRef: React.RefObject<HTMLDivElement | null>,
  setModalPosition: SetPos,
  options: DraggableOptions = { constrainToViewport: true, disableIframeDuringDrag: true }
) {
  const dragState = React.useRef<{
    dragging: boolean;
    startX: number;
    startY: number;
    origLeft: number;
    origTop: number;
    dx: number;
    dy: number;
    width: number;
    height: number;
    raf: number | null;
    iframe: HTMLIFrameElement | null;
  }>({ dragging: false, startX: 0, startY: 0, origLeft: 0, origTop: 0, dx: 0, dy: 0, width: 0, height: 0, raf: null, iframe: null });

  const pointerDown = (e: React.PointerEvent) => {
    if (!modalRef.current) return;
    const rect = modalRef.current.getBoundingClientRect();

    dragState.current.dragging = true;
    dragState.current.startX = e.clientX;
    dragState.current.startY = e.clientY;
    dragState.current.origLeft = rect.left;
    dragState.current.origTop = rect.top;
    dragState.current.dx = 0;
    dragState.current.dy = 0;
    dragState.current.width = rect.width;
    dragState.current.height = rect.height;

    // Improve paint performance
    modalRef.current.style.willChange = 'transform';
    // Ensure no conflicting transform animation
    modalRef.current.style.animation = 'none';

    // Optionally disable iframe interactions during drag
    if (options.disableIframeDuringDrag) {
      const iframe = modalRef.current.querySelector('iframe');
      if (iframe) {
        dragState.current.iframe = iframe as HTMLIFrameElement;
        dragState.current.iframe.style.pointerEvents = 'none';
      } else {
        dragState.current.iframe = null;
      }
    }

    (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
  };

  const pointerMove = (e: React.PointerEvent) => {
    if (!dragState.current.dragging || !modalRef.current) return;

    // Raw delta
    let dx = e.clientX - dragState.current.startX;
    let dy = e.clientY - dragState.current.startY;

    if (options.constrainToViewport) {
      const maxLeft = Math.max(0, window.innerWidth - dragState.current.width);
      const maxTop = Math.max(0, window.innerHeight - dragState.current.height);
      const wantedLeft = dragState.current.origLeft + dx;
      const wantedTop = dragState.current.origTop + dy;
      const clampedLeft = Math.min(Math.max(0, wantedLeft), maxLeft);
      const clampedTop = Math.min(Math.max(0, wantedTop), maxTop);
      dx = clampedLeft - dragState.current.origLeft;
      dy = clampedTop - dragState.current.origTop;
    }

    dragState.current.dx = dx;
    dragState.current.dy = dy;

    if (dragState.current.raf) cancelAnimationFrame(dragState.current.raf);
    dragState.current.raf = requestAnimationFrame(() => {
      if (!modalRef.current) return;
      modalRef.current.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
    });
  };

  const pointerUp = (e: React.PointerEvent) => {
    if (!dragState.current.dragging) return;
    dragState.current.dragging = false;

    // Snap to the exact visual rect before clearing transform
    if (modalRef.current) {
      const rectNow = modalRef.current.getBoundingClientRect();
      modalRef.current.style.left = `${rectNow.left}px`;
      modalRef.current.style.top = `${rectNow.top}px`;
      modalRef.current.style.right = 'auto';
      modalRef.current.style.bottom = 'auto';
      setModalPosition({ x: rectNow.left, y: rectNow.top });
    }

    if (dragState.current.raf) {
      cancelAnimationFrame(dragState.current.raf);
      dragState.current.raf = null;
    }
    if (modalRef.current) {
      modalRef.current.style.transform = '';
      modalRef.current.style.willChange = '';
    }
    if (dragState.current.iframe) {
      dragState.current.iframe.style.pointerEvents = '';
      dragState.current.iframe = null;
    }
    (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
  };

  return {
    onPointerDown: pointerDown,
    onPointerMove: pointerMove,
    onPointerUp: pointerUp,
  } as const;
}
