import { useVueFlow } from '@vue-flow/core'
import { ref, watch } from 'vue'
import type { XYPosition } from '@vue-flow/core'

/**
 * In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
 * @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
 */
const state = {
	/**
	 * The type of the node being dragged.
	 */
	draggedType: ref<string | null>(null),
	isDragOver: ref(false),
	isDragging: ref(false)
}

export default function useDragAndDrop(options?: {
	id: string
	addNodes: (nodes: { type: string; position: XYPosition }) => void
}) {
	const { draggedType, isDragOver, isDragging } = state
	const { id, addNodes } = options || {}

	const { screenToFlowCoordinate } = useVueFlow(id)

	watch(isDragging, (dragging) => {
		document.body.style.userSelect = dragging ? 'none' : ''
	})

	function onDragStart(event: DragEvent, type: string) {
		if (event.dataTransfer) {
			event.dataTransfer.setData('application/vueflow', type)
			event.dataTransfer.effectAllowed = 'move'
		}

		draggedType.value = type
		isDragging.value = true

		document.addEventListener('drop', onDragEnd)
	}

	/**
	 * Handles the drag over event.
	 *
	 * @param {DragEvent} event
	 */
	function onDragOver(event: DragEvent) {
		event.preventDefault()

		if (draggedType.value) {
			isDragOver.value = true

			if (event.dataTransfer) {
				event.dataTransfer.dropEffect = 'move'
			}
		}
	}

	function onDragLeave() {
		isDragOver.value = false
	}

	function onDragEnd() {
		isDragging.value = false
		isDragOver.value = false
		draggedType.value = null
		document.removeEventListener('drop', onDragEnd)
	}

	/**
	 * Handles the drop event.
	 *
	 * @param {DragEvent} event
	 */
	function onDrop(event: DragEvent) {
		const position = screenToFlowCoordinate({
			x: event.clientX,
			y: event.clientY
		})

		const newNode = {
			type: draggedType.value!,
			position
		}

		addNodes?.(newNode)
	}

	return {
		draggedType,
		isDragOver,
		isDragging,
		onDragStart,
		onDragLeave,
		onDragOver,
		onDrop
	}
}
