<script>
    import { arrayMoveElement } from "../helpers";
    import { flip } from "svelte/animate";

    let {
        sortableClass,
        onUpdate,
        items,
        itemView,
        itemCssClass,
        itemKey = "id",
        type = "list",
        handleClass = null,
        disabled = false,
    } = $props();
    // let sortableInstance = $state();
    let sortableContainer = $state();
    let draggedItem = $state();

    function handleDragStart(event, item) {
        if (disabled) {
            return false;
        }
        draggedItem = item;
        // Set data required for drag operation
        event.dataTransfer.effectAllowed = "move";
        event.dataTransfer.setData("text/plain", item.id);
        // Set a ghost drag image
        event.currentTarget.classList.add("dragging");
    }

    // Handle drag over another item
    function handleDragOver(event) {
        event.preventDefault();
        event.target.closest(".draggable-item").classList.add("dragover");
        event.dataTransfer.dropEffect = "move";
    }

    // Handle dropping the item
    function handleDrop(event, targetItem) {
        event.preventDefault();

        if (draggedItem === targetItem) return;
        // Find positions of dragged and target items
        const draggedIndex = items.findIndex(
            (item) => getItem(item, itemKey) === getItem(draggedItem, itemKey),
        );
        const targetIndex = items.findIndex(
            (item) => getItem(item, itemKey) === getItem(targetItem, itemKey),
        );

        onUpdate(
            arrayMoveElement([...items], draggedIndex, targetIndex),
            draggedIndex,
            targetIndex,
        );
    }

    function getItem(item, key) {
        if (key === null) {
            return item;
        }
        if (key.includes(".")) {
            return key.split(".").reduce((a, b) => a[b] ?? null, item);
        }
        return item[key];
    }

    function handleDragEnd(event) {
        event.target.classList.remove("dragging");
        sortableContainer
            .querySelector(".dragover")
            ?.classList.remove("dragover");
        draggedItem = null;
        const draggableItem = event.target.closest(".draggable-item");
        draggableItem.draggable = false;
    }

    function handleDragLeave(event) {
        // event.target.classList.remove("dragover");
        const draggableItem = event.target.closest(".draggable-item");
        draggableItem.classList.remove("dragover");
        draggableItem.draggable = false;
    }

    function handleMouseDown(e) {
        const handleEl = e.target.closest("." + handleClass);
        if (!handleEl) {
            return true;
        }
        const draggableItem = e.target.closest(".draggable-item");
        draggableItem.draggable = true;
    }

    function handleMouseUp(e) {
        const handleEl = e.target.closest("." + handleClass);
        if (!handleEl) {
            return true;
        }
        const draggableItem = e.target.closest(".draggable-item");
        draggableItem.draggable = false;
    }
</script>

{#if type === "table"}
    <tbody
        class="sortable-container {sortableClass}"
        bind:this={sortableContainer}
    >
        {#each items as item (getItem(item, itemKey))}
            <tr
                animate:flip={{ duration: 100 }}
                class="draggable-item {itemCssClass}"
                draggable="false"
                onmousedown={handleMouseDown}
                onmouseup={handleMouseUp}
                ondragstart={(e) => handleDragStart(e, item)}
                ondragover={handleDragOver}
                ondragleave={handleDragLeave}
                ondrop={(e) => handleDrop(e, item)}
                ondragend={handleDragEnd}
            >
                {@render itemView(item)}
            </tr>
        {/each}
    </tbody>
{:else}
    <div
        class="sortable-container {sortableClass}"
        bind:this={sortableContainer}
    >
        {#each items as item (getItem(item, itemKey))}
            <div
                animate:flip={{ duration: 100 }}
                role="listitem"
                class="draggable-item {itemCssClass}"
                draggable="false"
                onmousedown={handleMouseDown}
                onmouseup={handleMouseUp}
                ondragstart={(e) => handleDragStart(e, item)}
                ondragover={handleDragOver}
                ondragleave={handleDragLeave}
                ondrop={(e) => handleDrop(e, item)}
                ondragend={handleDragEnd}
            >
                {@render itemView(item)}
            </div>
        {/each}
    </div>
{/if}
