/**
 * Creates a reorderable list from any `container` and viable list item selector.
 *
 * This function is absolute magic and I love it
 *
 * Example:
 *  ```html
 *  <ul id="list">
 *      <li>Item 1</li>
 *      <li>Item 2</li>
 *      <li>Item 3</li>
 *  </ul>
 *  ```
 *  ```js
 *  // javascript
 *  makeMagicList(document.getElementById("list"), "li");
 *  ```
 *
 * @param {HTMLElement} container The parent container to use as a list.
 * @param {string} itemSelector The selector name of list item elements.
 * @param {Function} callback A function to call after each reordering.
 */
export function makeMagicList(container, itemSelector, callback) {
    if (!container)
        throw new Error("container not provided");
    if (!itemSelector)
        throw new Error("itemSelector not provided");

    container.querySelectorAll(itemSelector).forEach(item => {
        item.draggable = true;
        item.addEventListener("dragstart", () => { item.classList.add("moving") });
        item.addEventListener("dragend", () => { item.classList.remove("moving") });
        
        // dragging on inputs should take priority
        item.querySelectorAll("input").forEach(el => {
            el.addEventListener("mousedown", () => { item.draggable = false });
            el.addEventListener("mouseup", () => { item.draggable = true });
            el.addEventListener("dragstart", e => { e.stopPropagation() });
        });
    });

    var lastCursorY;
    container.addEventListener("dragover", event => {
        const dragging = container.querySelector(itemSelector + ".moving");
        if (!dragging) return;

        let cursorY = event.touches ? event.touches[0].clientY : event.clientY;

        // don't bother processing if we haven't moved
        if (lastCursorY === cursorY) return
        lastCursorY = cursorY;

        // get the element positioned ahead of the cursor
        const notMoving = [...container.querySelectorAll(itemSelector + ":not(.moving)")];
        const afterElement = notMoving.reduce((previous, current) => {
            const box = current.getBoundingClientRect();
            const offset = cursorY - box.top - box.height / 2;
            if (offset < 0 && offset > previous.offset)
                return { offset: offset, element: current };
            return previous;
        }, { offset: Number.NEGATIVE_INFINITY }).element;

        if (afterElement) {
            container.insertBefore(dragging, afterElement);
        } else {
            container.appendChild(dragging);
        }

        if (callback) callback();
    });
}
