import { DirectiveFunction, DirectiveOptions, VNode } from 'vue'
import Sortablejs from 'sortablejs'

const sortableMap = new WeakMap()

const emit = (vnode: VNode, ...args: any[]) => {
  if (vnode.data && vnode.data.on) {
    const handlers = vnode.data.on as { [key: string]: Function }

    if ('reorder' in handlers) {
      ;(handlers.reorder as Function & { fns: Function }).fns(...args)
    }
  }
}

const onSort = (element: HTMLElement, vnode: VNode) => (event: {
  oldIndex: number
  newIndex: number
}) => {
  if (!vnode || !vnode.data || !vnode.data.attrs) {
    return
  }

  const array = vnode.data.attrs.sorting

  if (!array) {
    return
  }

  const target = array[event.oldIndex]

  const deleted = array.splice(event.oldIndex, 1)
  array.splice(event.newIndex, 0, deleted[0])

  emit(vnode, {
    target,
    sorted: array,
    oldIndex: event.oldIndex,
    newIndex: event.newIndex,
  })
}

const Sortable: DirectiveOptions | DirectiveFunction = {
  bind: function(el, binding, vnode) {
    const config = binding.value || {}

    config.onSort = onSort(el, vnode)

    if (!('animation' in config)) {
      config.animation = 200
    }

    sortableMap.set(el, Sortablejs.create(el, config))
  },
  update(el, binding, vnode) {
    const sortable = sortableMap.get(el)

    sortable.option('onSort', onSort(el, vnode))
  },
}

export default Sortable
