<template>
	<TreeRoot v-bind="forward" v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 overflow-auto max-h-full">
		<DraggableTreeItem v-for="item in flattenItems" :key="item._id" v-bind="item" class="group flex items-center outline-none relative cursor-pointer max-w-full" @select.prevent @toggle.prevent>
			<template #default="{ handleToggle, handleSelect, isExpanded, isSelected, isDragging, isDraggedOver }">
				<slot :handleToggle="handleToggle"
						:handleSelect="handleSelect"
						:isExpanded="isExpanded"
						:isSelected="isSelected"
						:isDragging="isDragging"
						:isDraggedOver="isDraggedOver"
						:item="item"
					/>
			</template>
			<template #hint="{ instruction }">
				<div v-if="instruction">
					<slot name="hint" :instruction="instruction" />
				</div>
			</template>
		</DraggableTreeItem>
	</TreeRoot>
</template>

<script setup lang="ts" generic="T extends Record<string, any>">
import { useForwardPropsEmits, type FlattenedItem, type TreeRootEmits, type TreeRootProps } from 'radix-vue';
import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item'
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'

const props = defineProps<TreeRootProps<T>>();
const emits = defineEmits<TreeRootEmits<T> & {
		'updateTree': [instruction: Instruction, itemId: string, targetId: string];
}>();

defineSlots<{
	default: (props: {
		handleToggle: () => void, 
		handleSelect: () => void, 
		isExpanded: boolean, 
		isSelected: boolean, 
		isDragging: boolean, 
		isDraggedOver: boolean, 
		item: FlattenedItem<T>, 
	}) => any,
	hint: (props: {
		instruction: Extract<Instruction, { type: 'reorder-above' | 'reorder-below' | 'make-child' }> | null
	}) => any,
}>();

const forward = useForwardPropsEmits(props, emits);

watchEffect((onCleanup) => {
	const dndFunction = combine(
		monitorForElements({
			onDrop(args) {
				const { location, source } = args;
				
				if (!location.current.dropTargets.length)
					return;

				const itemId = source.data.id as string;
				const target = location.current.dropTargets[0];
				const targetId = target.data.id as string;

				const instruction: Instruction | null = extractInstruction(
					target.data,
				);

				if (instruction !== null)
				{
					emits('updateTree', instruction, itemId, targetId);
				}
			},
		}),
	)

	onCleanup(() => {
		dndFunction();
	})
})
</script>