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

interface UseDraggableOptions {
  /** 是否启用拖拽 */
  enabled?: boolean
  /** 拖拽边界限制 */
  boundary?: 'viewport' | 'parent' | HTMLElement | null
  /** 拖拽手柄选择器 */
  handle?: string
}

/**
 * 拖拽功能composable
 */
export function useDraggable(elementRef: Ref<HTMLElement | undefined>, options: UseDraggableOptions = {}) {
  const { enabled = true, boundary = 'viewport', handle } = options

  // 拖拽状态
  const isDragging = ref(false)
  const position = ref({ x: 0, y: 0 })

  // 拖拽相关变量
  let startX = 0
  let startY = 0
  let startLeft = 0
  let startTop = 0
  let dragElement: HTMLElement | null = null
  let handleElement: HTMLElement | null = null

  /**
   * 获取边界限制
   */
  const getBoundary = () => {
    if (!boundary || !dragElement)
      return null

    if (boundary === 'viewport') {
      return {
        left: 0,
        top: 0,
        right: window.innerWidth,
        bottom: window.innerHeight,
      }
    }

    if (boundary === 'parent' && dragElement.parentElement) {
      const parent = dragElement.parentElement
      const rect = parent.getBoundingClientRect()
      return {
        left: 0,
        top: 0,
        right: rect.width,
        bottom: rect.height,
      }
    }

    if (boundary instanceof HTMLElement) {
      const rect = boundary.getBoundingClientRect()
      return {
        left: 0,
        top: 0,
        right: rect.width,
        bottom: rect.height,
      }
    }

    return null
  }

  /**
   * 更新元素位置
   */
  const updateElementPosition = () => {
    if (!dragElement)
      return

    dragElement.style.transform = `translate(${position.value.x}px, ${position.value.y}px)`
  }

  /**
   * 限制位置在边界内
   */
  const constrainPosition = (x: number, y: number) => {
    if (!dragElement)
      return { x, y }

    const bounds = getBoundary()
    if (!bounds)
      return { x, y }

    const rect = dragElement.getBoundingClientRect()

    const minX = bounds.left
    const maxX = bounds.right - rect.width
    const minY = bounds.top
    const maxY = bounds.bottom - rect.height

    return {
      x: Math.max(minX, Math.min(maxX, x)),
      y: Math.max(minY, Math.min(maxY, y)),
    }
  }

  /**
   * 拖拽移动
   */
  const handleMouseMove = (e: MouseEvent) => {
    if (!isDragging.value)
      return

    const deltaX = e.clientX - startX
    const deltaY = e.clientY - startY

    const newPosition = constrainPosition(
      startLeft + deltaX,
      startTop + deltaY,
    )

    position.value = newPosition
    updateElementPosition()
  }

  const handleTouchMove = (e: TouchEvent) => {
    if (!isDragging.value)
      return

    e.preventDefault()

    const deltaX = e.touches[0].clientX - startX
    const deltaY = e.touches[0].clientY - startY

    const newPosition = constrainPosition(
      startLeft + deltaX,
      startTop + deltaY,
    )

    position.value = newPosition
    updateElementPosition()
  }

  /**
   * 结束拖拽
   */
  function handleMouseUp() {
    if (!isDragging.value)
      return

    isDragging.value = false

    // 移除全局事件监听
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
    document.removeEventListener('touchmove', handleTouchMove)
    document.removeEventListener('touchend', handleMouseUp)

    // 移除拖拽样式
    if (dragElement) {
      dragElement.style.userSelect = ''
      dragElement.style.pointerEvents = ''
    }
    document.body.style.userSelect = ''
  }

  const handleTouchEnd = handleMouseUp

  /**
   * 开始拖拽
   */
  const handleMouseDown = (e: MouseEvent | TouchEvent) => {
    if (!enabled || !dragElement)
      return

    // 检查是否点击在拖拽手柄上
    if (handle && handleElement) {
      const target = e.target as HTMLElement
      if (!handleElement.contains(target))
        return
    }

    e.preventDefault()

    const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX
    const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY

    startX = clientX
    startY = clientY
    startLeft = position.value.x
    startTop = position.value.y

    isDragging.value = true

    // 添加全局事件监听
    document.addEventListener('mousemove', handleMouseMove)
    document.addEventListener('mouseup', handleMouseUp)
    document.addEventListener('touchmove', handleTouchMove, { passive: false })
    document.addEventListener('touchend', handleTouchEnd)

    // 添加拖拽样式
    dragElement.style.userSelect = 'none'
    dragElement.style.pointerEvents = 'none'
    document.body.style.userSelect = 'none'
  }

  /**
   * 设置位置
   */
  const setPosition = (x: number, y: number) => {
    const newPosition = constrainPosition(x, y)
    position.value = newPosition
    updateElementPosition()
  }

  /**
   * 重置位置
   */
  const resetPosition = () => {
    position.value = { x: 0, y: 0 }
    updateElementPosition()
  }

  /**
   * 初始化拖拽
   */
  const initDraggable = () => {
    if (!elementRef.value)
      return

    dragElement = elementRef.value
    handleElement = handle ? dragElement.querySelector(handle) : dragElement

    if (!handleElement)
      handleElement = dragElement

    // 设置初始样式
    dragElement.style.position = 'absolute'
    dragElement.style.cursor = 'move'

    // 添加事件监听
    handleElement.addEventListener('mousedown', handleMouseDown)
    handleElement.addEventListener('touchstart', handleMouseDown, { passive: false })
  }

  /**
   * 清理拖拽
   */
  const cleanupDraggable = () => {
    if (handleElement) {
      handleElement.removeEventListener('mousedown', handleMouseDown)
      handleElement.removeEventListener('touchstart', handleMouseDown)
    }

    // 清理全局事件监听
    document.removeEventListener('mousemove', handleMouseMove)
    document.removeEventListener('mouseup', handleMouseUp)
    document.removeEventListener('touchmove', handleTouchMove)
    document.removeEventListener('touchend', handleTouchEnd)
  }

  // 生命周期
  onMounted(() => {
    if (enabled) {
      initDraggable()
    }
  })

  onUnmounted(() => {
    cleanupDraggable()
  })

  return {
    isDragging,
    position,
    setPosition,
    resetPosition,
    initDraggable,
    cleanupDraggable,
  }
}
