import { ref, onUnmounted, type Ref } from 'vue'

interface UseDraggableOptions {
  boundary?: 'none' | 'parent'
  threshold?: number
  onDragStart?: () => void
  onDragEnd?: (moved: boolean) => void
}

export function useDraggable(
  targetRef: Ref<HTMLElement | undefined>,
  options: UseDraggableOptions = {}
) {
  const isDragging = ref(false)
  const hasMoved = ref(false)
  const suppressClick = ref(false)
  const startX = ref(0)
  const startY = ref(0)
  const startLeft = ref(0)
  const startTop = ref(0)

  const boundary = options.boundary ?? 'none'
  const threshold = options.threshold ?? 3

  const getParent = (el: HTMLElement) => {
    return (el.offsetParent as HTMLElement | null) ?? document.body
  }

  const cleanup = () => {
    isDragging.value = false
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
  }

  const handleMouseDown = (e: MouseEvent) => {
    if (e.button !== 0 || !targetRef.value) return

    e.preventDefault()

    const target = targetRef.value
    const targetRect = target.getBoundingClientRect()
    const parentRect = getParent(target).getBoundingClientRect()

    isDragging.value = true
    hasMoved.value = false
    suppressClick.value = false
    startX.value = e.clientX
    startY.value = e.clientY
    startLeft.value = targetRect.left - parentRect.left
    startTop.value = targetRect.top - parentRect.top

    options.onDragStart?.()

    document.addEventListener('mousemove', handleMouseMove)
    document.addEventListener('mouseup', handleMouseUp)
  }

  const handleMouseMove = (e: MouseEvent) => {
    if (!isDragging.value || !targetRef.value) return

    const target = targetRef.value
    const parent = getParent(target)
    const deltaX = e.clientX - startX.value
    const deltaY = e.clientY - startY.value

    if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
      hasMoved.value = true
    }

    let nextLeft = startLeft.value + deltaX
    let nextTop = startTop.value + deltaY

    if (boundary === 'parent') {
      const maxLeft = Math.max(parent.clientWidth - target.offsetWidth, 0)
      const maxTop = Math.max(parent.clientHeight - target.offsetHeight, 0)
      nextLeft = Math.min(Math.max(nextLeft, 0), maxLeft)
      nextTop = Math.min(Math.max(nextTop, 0), maxTop)
    }

    target.style.left = `${nextLeft}px`
    target.style.top = `${nextTop}px`
    target.style.right = 'auto'
    target.style.bottom = 'auto'
  }

  const handleMouseUp = () => {
    suppressClick.value = hasMoved.value
    cleanup()
    options.onDragEnd?.(hasMoved.value)
  }

  const consumeClickSuppression = () => {
    const value = suppressClick.value
    suppressClick.value = false
    return value
  }

  onUnmounted(() => {
    cleanup()
  })

  return {
    isDragging,
    hasMoved,
    handleMouseDown,
    consumeClickSuppression
  }
}
