import { ref } from 'vue'

interface DragReorderHandlers {
  draggingIndex: Ref<number | null>
  dropTargetIndex: Ref<number | null>
  onDragStart: (index: number, event: DragEvent) => void
  onDragEnter: (index: number) => void
  onDragOver: (event: DragEvent) => void
  onDrop: (index: number) => void
  onDragEnd: () => void
  reorderClass: (index: number) => string
  reset: () => void
}

interface DragReorderOptions {
  draggingClass?: string
  dropTargetClass?: string
  onReorder?: () => void
}

function moveItemInPlace<T>(list: T[], from: number, to: number): void {
  if (from === to) return
  if (from < 0 || to < 0 || from >= list.length || to >= list.length) return
  const updated = list.slice()
  const [item] = updated.splice(from, 1)
  if (item === undefined) return
  updated.splice(to, 0, item)
  list.splice(0, list.length, ...updated)
}

export function useDragReorder(
  getList: () => unknown[] | undefined,
  options: DragReorderOptions = {},
): DragReorderHandlers {
  const {
    draggingClass = 'border-dashed border-primary',
    dropTargetClass = 'border-primary border-dashed bg-primary/5',
    onReorder,
  } = options

  const draggingIndex = ref<number | null>(null)
  const dropTargetIndex = ref<number | null>(null)

  const reset = () => {
    draggingIndex.value = null
    dropTargetIndex.value = null
  }

  const onDragStart = (index: number, event: DragEvent) => {
    draggingIndex.value = index
    dropTargetIndex.value = index
    if (event.dataTransfer) {
      event.dataTransfer.effectAllowed = 'move'
    }
  }

  const onDragEnter = (index: number) => {
    if (draggingIndex.value === null) return
    dropTargetIndex.value = index
  }

  const onDragOver = (event: DragEvent) => {
    event.preventDefault()
  }

  const onDrop = (index: number) => {
    const list = getList()
    if (!Array.isArray(list)) {
      reset()
      return
    }
    const from = draggingIndex.value
    if (from === null) {
      reset()
      return
    }
    moveItemInPlace(list, from, index)
    onReorder?.()
    reset()
  }

  const onDragEnd = () => {
    reset()
  }

  const reorderClass = (index: number): string => {
    if (draggingIndex.value === index) return draggingClass
    if (
      draggingIndex.value !== null
      && dropTargetIndex.value === index
      && draggingIndex.value !== index
    ) {
      return dropTargetClass
    }
    return ''
  }

  return {
    draggingIndex,
    dropTargetIndex,
    onDragStart,
    onDragEnter,
    onDragOver,
    onDrop,
    onDragEnd,
    reorderClass,
    reset,
  }
}
