import { useDrag } from '@use-gesture/react'
import { memo, useEffect, useRef, useState, useCallback } from 'react'
import { cn } from '@/utils/cn'

export interface DraggableProps {
  className?: string
  children: React.ReactNode
  target?: string
  bounds?:
    | 'parent'
    | 'window'
    | { left: number; top: number; right: number; bottom: number }
  offset?: { x: number; y: number }
  innerRef?: (node: HTMLDivElement | null) => void
  style?: React.CSSProperties
}

export const Draggable = memo(function Draggable(props: DraggableProps) {
  const {
    className,
    children,
    target,
    bounds = 'window',
    offset,
    innerRef,
    style,
  } = props
  const rootRef = useRef<HTMLDivElement | null>(null)
  const [isDragging, setIsDragging] = useState(false)

  // 响应外部 offset 变化
  useEffect(() => {
    if (offset && rootRef.current) {
      rootRef.current.style.left = `${offset.x}px`
      rootRef.current.style.top = `${offset.y}px`
    }
  }, [offset])

  const bind = useDrag(
    ({ movement: [mx, my], first, last, memo, event, cancel }) => {
      const el = rootRef.current
      if (!el) return

      // 1. 过滤掉交互性元素的拖拽请求（如按钮、输入框等）
      if (first) {
        const targetElement = event.target as HTMLElement
        const isInteractive =
          ['INPUT', 'TEXTAREA', 'BUTTON', 'SELECT'].includes(
            targetElement.tagName,
          ) ||
          targetElement.closest('button') ||
          targetElement.closest('[data-no-drag]')

        if (isInteractive) {
          cancel()
          return
        }
      }

      // 2. 处理句柄逻辑
      if (first && target) {
        const handle = el.querySelector(target)
        if (handle && !handle.contains(event.target as Node)) {
          cancel()
          return
        }
      }

      // 3. 拖拽开始：实时从 DOM 获取起点坐标，解决“跳动”问题的关键
      if (first) {
        setIsDragging(true)
        const s = window.getComputedStyle(el)
        const l = parseInt(s.left)
        const t = parseInt(s.top)

        // 即时获取物理坐标
        const curX = isNaN(l) ? el.offsetLeft : l
        const curY = isNaN(t) ? el.offsetTop : t

        const rect = el.getBoundingClientRect()
        return {
          startX: curX,
          startY: curY,
          dx: rect.left - curX, // 屏幕坐标与 style 坐标的差值
          dy: rect.top - curY,
          width: rect.width,
          height: rect.height,
        }
      }

      if (last) {
        setIsDragging(false)
      }

      // 如果 memo 没初始化（比如被取消了），不执行后续逻辑
      if (!memo) return

      // 3. 计算新坐标
      let x = memo.startX + mx
      let y = memo.startY + my

      // 4. 边界处理
      if (bounds === 'window') {
        const { dx, dy, width, height } = memo
        x = Math.max(-dx, Math.min(window.innerWidth - width - dx, x))
        y = Math.max(-dy, Math.min(window.innerHeight - height - dy, y))
      }

      // 5. 直接更新 DOM 提升性能
      el.style.left = `${x}px`
      el.style.top = `${y}px`

      return memo
    },
    {
      pointer: { capture: true },
      filterTaps: true, // 过滤掉无位移点击
    },
  )

  const setRefs = useCallback(
    (node: HTMLDivElement | null) => {
      rootRef.current = node
      innerRef?.(node)
    },
    [innerRef],
  )

  return (
    <div
      {...bind()}
      ref={setRefs}
      className={cn('w-fit h-fit', className)}
      style={{
        position: 'absolute',
        touchAction: 'none',
        userSelect: isDragging ? 'none' : 'auto',
        transition: isDragging ? 'none' : undefined, // 拖拽时必须禁用过渡
        ...style,
      }}
    >
      {children}
    </div>
  )
})
