/* eslint-disable @typescript-eslint/no-explicit-any */
// #region Imports
import { getHorizontalDropIndex, getMixedDropIndex, getVerticalDropIndex } from '../Utilities/CollisionDetection';
import { applyTransform, clearTransform } from '../Utilities/PositionUtils';
import { getDragDropRegistry } from './DragDropRegistry';
// #endregion
// #region DropListRef
/**
 * Reference to a drop list container.
 * Manages drag items, sorting, and drop events.
 *
 * @public
 */
export class DropListRef {
    // #region Fields
    _config;
    _element;
    _data;
    _items = [];
    _connectedLists = [];
    _activeDragItem = null;
    _itemPositions = [];
    _isReceiving = false;
    _registry = getDragDropRegistry();
    _currentPlaceholderIndex = -1;
    // #endregion
    // #region Ctor
    /**
     * Create a new DropListRef.
     *
     * @param element - Host element.
     * @param config - Configuration options.
     */
    constructor(element, config) {
        console.log('%c[DropListRef] Constructor', 'color: #673AB7; font-weight: bold', {
            element: element.tagName,
            className: element.className,
            config
        });
        this._element = element;
        this._config = this._applyConfigDefaults(config ?? {});
        this._data = config?.data;
        // Register with registry
        this._registry.registerDropContainer(this);
        console.log('%c[DropListRef] Registered with id', 'color: #673AB7', { id: this._config.id });
    }
    // #endregion
    // #region Properties
    /**
     * Get host element.
     *
     * @public
     */
    get element() {
        return this._element;
    }
    /**
     * Get associated data.
     *
     * @public
     */
    get data() {
        return this._data;
    }
    /**
     * Set associated data.
     *
     * @public
     */
    set data(value) {
        this._data = value;
    }
    /**
     * Get disabled state.
     *
     * @public
     */
    get disabled() {
        return this._config.disabled;
    }
    /**
     * Set disabled state.
     *
     * @public
     */
    set disabled(value) {
        this._config.disabled = value;
    }
    /**
     * Get orientation.
     *
     * @public
     */
    get orientation() {
        return this._config.orientation;
    }
    /**
     * Set orientation.
     *
     * @public
     */
    set orientation(value) {
        this._config.orientation = value;
    }
    /**
     * Get items.
     *
     * @public
     */
    get items() {
        return this._items;
    }
    /**
     * Get connected lists.
     *
     * @public
     */
    get connectedTo() {
        return this._connectedLists;
    }
    /**
     * Whether this list is currently receiving an item from another list.
     *
     * @public
     */
    get isReceiving() {
        return this._isReceiving;
    }
    /**
     * Get ID.
     *
     * @public
     */
    get id() {
        return this._config.id;
    }
    // #endregion
    // #region Methods
    /**
     * Add an item to this list.
     *
     * @param item - Item to add.
     *
     * @public
     */
    withItems(items) {
        this._items = items;
        // Tie items to this container
        items.forEach((item) => {
            item.withDropContainer(this);
        });
        return this;
    }
    /**
     * Connect to other drop lists.
     *
     * @param lists - Connected lists.
     *
     * @public
     */
    connectTo(lists) {
        this._connectedLists = lists;
        return this;
    }
    /**
     * Set orientation (fluent API).
     *
     * @param orientation - List orientation.
     *
     * @public
     */
    withOrientation(orientation) {
        this._config.orientation = orientation;
        return this;
    }
    /**
     * Check if drag container is currently dragging.
     *
     * @returns True if dragging active.
     *
     * @public
     */
    isDragging() {
        return this._activeDragItem !== null;
    }
    /**
     * Set scrollable parent elements.
     *
     * @param parents - Array of scrollable parent elements.
     *
     * @public
     */
    withScrollableParents(parents) {
        // Store for future auto-scroll implementation
        this._config.scrollableParents = parents;
        return this;
    }
    /**
     * Set element container.
     *
     * @param container - Container element.
     *
     * @public
     */
    withElementContainer(container) {
        // Store for future reference
        this._config.elementContainer = container;
        return this;
    }
    /**
     * Set layout direction (ltr/rtl).
     *
     * @param direction - Layout direction.
     *
     * @public
     */
    withDirection(direction) {
        this._config.direction = direction;
        return this;
    }
    /**
     * Start drag sequence manually.
     * Notifies the drop list that dragging has started.
     *
     * @public
     */
    start() {
        // Mark as active - mainly for state management
        // Actual drag is initiated by DragRef
        if (!this._config.disabled) {
            this._cacheItemPositions();
        }
    }
    /**
     * Dispose of this drop list.
     *
     * @public
     */
    dispose() {
        this._registry.removeDropContainer(this);
        this._items = [];
        this._connectedLists = [];
        this._activeDragItem = null;
        this._itemPositions = [];
        this._currentPlaceholderIndex = -1;
    }
    /**
     * Called when an item in this container starts being dragged.
     *
     * @param item - Item that started dragging.
     *
     * @internal
     */
    _startDragging(item) {
        console.log('%c[DropListRef] _startDragging', 'color: #FF5722; font-weight: bold', {
            id: this._config.id,
            itemsCount: this._items.length,
            itemIndex: this._items.indexOf(item)
        });
        this._activeDragItem = item;
        this._currentPlaceholderIndex = this._items.indexOf(item);
        this._cacheItemPositions();
        console.log('%c[DropListRef] _startDragging - positions cached', 'color: #FF5722', {
            positionsCount: this._itemPositions.length,
            currentPlaceholderIndex: this._currentPlaceholderIndex
        });
    }
    /**
     * Check if this list can receive a drag item.
     *
     * @param item - Item to check.
     * @param pointerPosition - Current pointer position.
     * @returns Whether item can be received.
     *
     * @public
     */
    _canReceive(item, pointerPosition) {
        console.log('%c[DropListRef] _canReceive', 'color: #009688', {
            id: this._config.id,
            disabled: this._config.disabled,
            hasEnterPredicate: !!this._config.enterPredicate,
            connectedListsCount: this._connectedLists.length
        });
        if (this._config.disabled) {
            console.log('%c[DropListRef] _canReceive BLOCKED - disabled', 'color: #f44336');
            return false;
        }
        // Check custom predicate
        if (this._config.enterPredicate) {
            const result = this._config.enterPredicate(item, this);
            console.log('%c[DropListRef] _canReceive - enterPredicate result', 'color: #009688', { result });
            return result;
        }
        // Check if item is from a connected list
        const sourceContainer = item._dropContainer;
        console.log('%c[DropListRef] _canReceive - checking source', 'color: #009688', {
            sourceId: sourceContainer ? sourceContainer._config?.id : 'none',
            isSameContainer: sourceContainer === this
        });
        if (sourceContainer === this) {
            console.log('%c[DropListRef] _canReceive - same container, returning TRUE', 'color: #4CAF50');
            return true;
        }
        if (!sourceContainer) {
            console.log('%c[DropListRef] _canReceive - no source container, returning FALSE', 'color: #f44336');
            return false;
        }
        // Check if source is in our connected lists
        const isConnected = this._connectedLists.includes(sourceContainer);
        console.log('%c[DropListRef] _canReceive - connection check', 'color: #009688', {
            isConnected,
            connectedIds: this._connectedLists.map((l) => l._config?.id)
        });
        return isConnected;
    }
    /**
     * Handle item entering this list.
     *
     * @param item - Item entering.
     * @param pointerPosition - Pointer position.
     *
     * @internal
     */
    _enter(item, pointerPosition) {
        console.log('%c[DropListRef] _enter', 'color: #4CAF50; font-weight: bold', {
            id: this._config.id,
            pointerPosition,
            currentItemsCount: this._items.length
        });
        this._activeDragItem = item;
        const sourceContainer = item._dropContainer;
        this._isReceiving = sourceContainer !== this;
        console.log('%c[DropListRef] _enter - receiving status', 'color: #4CAF50', {
            isReceiving: this._isReceiving,
            sourceId: sourceContainer ? sourceContainer._config?.id : 'none'
        });
        // Add item to array if from another list
        if (this._isReceiving) {
            this._items.push(item);
            console.log('%c[DropListRef] _enter - item added to array', 'color: #4CAF50', {
                newItemsCount: this._items.length
            });
        }
        // Move placeholder to this container if receiving from another list
        const placeholder = item.getPlaceholderElement();
        if (placeholder && this._isReceiving) {
            // Determine insertion position
            const insertionIndex = this._getItemIndexAtPointForInsertion(pointerPosition);
            console.log('%c[DropListRef] _enter - inserting placeholder', 'color: #4CAF50', {
                insertionIndex,
                placeholderExists: !!placeholder
            });
            this._insertPlaceholderAtIndex(placeholder, insertionIndex);
            this._currentPlaceholderIndex = insertionIndex;
        }
        // Cache positions after placeholder is in place
        this._cacheItemPositions();
        // Perform initial sort to position placeholder correctly
        this._sortItem(item, pointerPosition);
        // Add class
        this._element.classList.add('cdk-drop-list-receiving');
        console.log('%c[DropListRef] _enter COMPLETE', 'color: #4CAF50; font-weight: bold');
    }
    /**
     * Handle item exiting this list.
     *
     * @param item - Item exiting.
     *
     * @internal
     */
    _exit(item) {
        console.log('%c[DropListRef] _exit', 'color: #FF9800; font-weight: bold', {
            id: this._config.id,
            isReceiving: this._isReceiving,
            itemsCount: this._items.length
        });
        // Remove item if it was received from another list
        if (this._isReceiving) {
            const index = this._items.indexOf(item);
            if (index > -1) {
                this._items.splice(index, 1);
                console.log('%c[DropListRef] _exit - item removed from array', 'color: #FF9800', {
                    removedIndex: index,
                    newItemsCount: this._items.length
                });
            }
        }
        this._activeDragItem = null;
        this._isReceiving = false;
        this._itemPositions = [];
        this._currentPlaceholderIndex = -1;
        this._element.classList.remove('cdk-drop-list-receiving');
        console.log('%c[DropListRef] _exit COMPLETE', 'color: #FF9800; font-weight: bold');
    }
    /**
     * Sort item in the list.
     *
     * @param item - Item to sort.
     * @param pointerPosition - Pointer position.
     *
     * @internal
     */
    _sortItem(item, pointerPosition) {
        console.log('%c[DropListRef] _sortItem', 'color: #2196F3; font-weight: bold', {
            id: this._config.id,
            pointerPosition,
            sortingDisabled: this._config.sortingDisabled,
            itemsCount: this._items.length,
            currentPlaceholderIndex: this._currentPlaceholderIndex
        });
        if (this._config.sortingDisabled) {
            console.log('%c[DropListRef] _sortItem SKIPPED - sorting disabled', 'color: #f44336');
            return;
        }
        if (this._items.length === 0) {
            console.log('%c[DropListRef] _sortItem SKIPPED - no items', 'color: #f44336');
            return;
        }
        const currentIndex = this._getItemIndex(item);
        if (currentIndex === -1) {
            console.log('%c[DropListRef] _sortItem SKIPPED - item not found', 'color: #f44336');
            return;
        }
        // Calculate new index based on orientation
        const newIndex = this._getItemIndexAtPointForSort(pointerPosition, item);
        console.log('%c[DropListRef] _sortItem - index calculation', 'color: #2196F3', {
            currentIndex,
            newIndex,
            currentPlaceholderIndex: this._currentPlaceholderIndex
        });
        if (newIndex !== -1 && newIndex !== this._currentPlaceholderIndex) {
            const previousIndex = this._currentPlaceholderIndex >= 0 ? this._currentPlaceholderIndex : currentIndex;
            console.log('%c[DropListRef] _sortItem - MOVING', 'color: #2196F3; font-weight: bold', {
                from: previousIndex,
                to: newIndex
            });
            // FLIP Animation: First - Cache positions of sibling items BEFORE placeholder move
            const siblingPositions = this._cacheSiblingPositions(item);
            // Move item in array
            this._items.splice(currentIndex, 1);
            this._items.splice(newIndex, 0, item);
            // Move placeholder in DOM
            const placeholder = item.getPlaceholderElement();
            if (placeholder) {
                this._movePlaceholderToIndex(placeholder, newIndex, item);
                console.log('%c[DropListRef] _sortItem - placeholder moved in DOM', 'color: #2196F3');
            }
            this._currentPlaceholderIndex = newIndex;
            // FLIP Animation: Last - Get new positions, Invert & Play
            this._animateSiblingItems(item, siblingPositions);
            // Emit sort event
            this._emitSortEvent(item, previousIndex, newIndex, pointerPosition);
            // Recompute positions after DOM change
            this._cacheItemPositions();
            console.log('%c[DropListRef] _sortItem COMPLETE - positions recached', 'color: #2196F3; font-weight: bold');
        }
        else {
            console.log('%c[DropListRef] _sortItem - no move needed', 'color: #2196F3');
        }
    }
    /**
     * Handle item drop.
     *
     * @param item - Item being dropped.
     * @param pointerPosition - Pointer position.
     *
     * @internal
     */
    _drop(item, pointerPosition) {
        console.log('%c[DropListRef] _drop', 'color: #E91E63; font-weight: bold', {
            id: this._config.id,
            pointerPosition,
            itemsCount: this._items.length
        });
        const currentIndex = this._getItemIndex(item);
        const previousContainer = item._dropContainer;
        const previousIndex = previousContainer ? previousContainer._getItemIndex(item) : -1;
        console.log('%c[DropListRef] _drop - indices', 'color: #E91E63', {
            currentIndex,
            previousIndex,
            previousContainerId: previousContainer ? previousContainer._config?.id : 'none',
            isTransfer: previousContainer !== this
        });
        // Update the item's container reference if it changed
        if (previousContainer !== this) {
            // Remove from previous container's items array
            if (previousContainer) {
                const prevIndex = previousContainer._items.indexOf(item);
                if (prevIndex > -1) {
                    previousContainer._items.splice(prevIndex, 1);
                    console.log('%c[DropListRef] _drop - removed from previous container', 'color: #E91E63');
                }
            }
            // Update the item's container reference
            item._dropContainer = this;
            console.log('%c[DropListRef] _drop - container reference updated', 'color: #E91E63');
        }
        // Emit drop event
        const detail = {
            type: 'drop',
            item,
            container: this,
            previousContainer: previousContainer,
            currentIndex,
            previousIndex,
            isPointerOverContainer: this._isPointerOverContainer(pointerPosition),
            distance: {
                x: 0,
                y: 0
            } // Would be calculated from DragRef
        };
        console.log('%c[DropListRef] _drop - dispatching drop event', 'color: #E91E63; font-weight: bold', {
            detail: {
                currentIndex: detail.currentIndex,
                previousIndex: detail.previousIndex,
                isPointerOverContainer: detail.isPointerOverContainer
            }
        });
        const event = new CustomEvent('drop', {
            bubbles: true,
            composed: true,
            cancelable: false,
            detail
        });
        this._element.dispatchEvent(event);
        // Cleanup
        this._activeDragItem = null;
        this._isReceiving = false;
        this._itemPositions = [];
        this._currentPlaceholderIndex = -1;
        this._element.classList.remove('cdk-drop-list-receiving');
        console.log('%c[DropListRef] _drop COMPLETE', 'color: #E91E63; font-weight: bold');
    }
    /**
     * Get the index of an item.
     *
     * @param item - Item.
     * @returns Index or -1.
     *
     * @internal
     */
    _getItemIndex(item) {
        return this._items.indexOf(item);
    }
    // #endregion
    // #region Private Methods - Position Caching
    /**
     * Cache positions of all items.
     * Uses placeholder rect for the active drag item since the original element is hidden.
     *
     * @private
     */
    _cacheItemPositions() {
        console.log('%c[DropListRef] _cacheItemPositions', 'color: #9C27B0', {
            id: this._config.id,
            itemsCount: this._items.length,
            hasActiveDragItem: !!this._activeDragItem
        });
        this._itemPositions = this._items.map((drag) => {
            // For the active drag item, use placeholder position if available
            let rect;
            if (drag === this._activeDragItem) {
                const placeholder = drag.getPlaceholderElement();
                rect = placeholder
                    ? placeholder.getBoundingClientRect()
                    : drag.element.getBoundingClientRect();
            }
            else {
                rect = drag.element.getBoundingClientRect();
            }
            return {
                drag,
                rect,
                center: {
                    x: rect.left + (rect.width / 2),
                    y: rect.top + (rect.height / 2)
                }
            };
        });
        console.log('%c[DropListRef] _cacheItemPositions - cached', 'color: #9C27B0', {
            positions: this._itemPositions.map((p) => ({
                width: Math.round(p.rect.width),
                height: Math.round(p.rect.height),
                top: Math.round(p.rect.top),
                left: Math.round(p.rect.left)
            }))
        });
    }
    /**
     * Cache positions of sibling items (excludes the dragged item).
     * Used for FLIP animation during sorting.
     *
     * @param draggedItem - The item being dragged.
     * @returns Map of DragRef to DOMRect.
     *
     * @private
     */
    _cacheSiblingPositions(draggedItem) {
        const positions = new Map();
        for (const drag of this._items) {
            if (drag !== draggedItem) {
                positions.set(drag, drag.element.getBoundingClientRect());
            }
        }
        return positions;
    }
    /**
     * Animate sibling items using FLIP technique.
     * Applies transforms to move items visually back to old positions,
     * then removes transforms in next frame to let items animate to new positions.
     *
     * @param draggedItem - The item being dragged.
     * @param oldPositions - Cached positions before placeholder move.
     *
     * @private
     */
    _animateSiblingItems(draggedItem, oldPositions) {
        // Get new positions after DOM change
        const itemsToAnimate = [];
        for (const drag of this._items) {
            if (drag !== draggedItem) {
                const oldRect = oldPositions.get(drag);
                if (oldRect) {
                    const newRect = drag.element.getBoundingClientRect();
                    const delta = {
                        x: oldRect.left - newRect.left,
                        y: oldRect.top - newRect.top
                    };
                    // Only animate if there's actual movement
                    if (Math.abs(delta.x) > 0.5 || Math.abs(delta.y) > 0.5) {
                        itemsToAnimate.push({
                            element: drag.element,
                            delta
                        });
                    }
                }
            }
        }
        if (itemsToAnimate.length === 0) {
            return;
        }
        console.log('%c[DropListRef] _animateSiblingItems', 'color: #FF9800', {
            count: itemsToAnimate.length
        });
        // FLIP: Invert - Apply transforms to move items back to old positions
        for (const { element, delta } of itemsToAnimate) {
            // Disable transitions temporarily
            element.style.transition = 'none';
            applyTransform(element, delta);
        }
        // Force reflow to apply transforms immediately
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        itemsToAnimate[0].element.getBoundingClientRect();
        // FLIP: Play - Remove transforms and enable transitions to animate to new positions
        requestAnimationFrame(() => {
            for (const { element } of itemsToAnimate) {
                element.style.transition = 'transform 250ms cubic-bezier(0.25, 0.8, 0.25, 1)';
                clearTransform(element);
            }
            // Cleanup transitions after animation
            setTimeout(() => {
                for (const { element } of itemsToAnimate) {
                    element.style.transition = '';
                }
            }, 250);
        });
    }
    /**
     * Get item index at a point.
     *
     * @param point - Point to check.
     * @returns Item index.
     *
     * @private
     */
    _getItemIndexAtPoint(point) {
        if (this._itemPositions.length === 0) {
            return -1;
        }
        const orientation = this._config.orientation;
        if (orientation === 'horizontal') {
            return getHorizontalDropIndex(this._itemPositions.map((p) => p.rect), point);
        }
        else if (orientation === 'vertical') {
            return getVerticalDropIndex(this._itemPositions.map((p) => p.rect), point);
        }
        else {
            // Mixed
            return getMixedDropIndex(this._itemPositions.map((p) => p.rect), point);
        }
    }
    /**
     * Check if pointer is over container element.
     *
     * @param point - Pointer position.
     * @returns True if over container.
     *
     * @private
     */
    _isPointerOverContainer(point) {
        const rect = this._element.getBoundingClientRect();
        return (point.x >= rect.left &&
            point.x <= rect.right &&
            point.y >= rect.top &&
            point.y <= rect.bottom);
    }
    /**
     * Get item index at a point for initial insertion (excluding the dragged item).
     *
     * @param point - Point to check.
     * @returns Insertion index.
     *
     * @private
     */
    _getItemIndexAtPointForInsertion(point) {
        // For insertion, we use the sibling elements (not including the dragged item)
        const siblingElements = this._getSiblingElements(null);
        if (siblingElements.length === 0) {
            return 0;
        }
        const rects = siblingElements.map((el) => el.getBoundingClientRect());
        const orientation = this._config.orientation;
        if (orientation === 'horizontal') {
            return getHorizontalDropIndex(rects, point);
        }
        else if (orientation === 'vertical') {
            return getVerticalDropIndex(rects, point);
        }
        else {
            return getMixedDropIndex(rects, point);
        }
    }
    /**
     * Get item index at a point for sorting (uses current sibling positions).
     *
     * @param point - Point to check.
     * @param dragItem - The item being dragged.
     * @returns New index for the item.
     *
     * @private
     */
    _getItemIndexAtPointForSort(point, dragItem) {
        // Get sibling elements excluding the dragged item's original element
        const siblingElements = this._getSiblingElements(dragItem);
        console.log('%c[DropListRef] _getItemIndexAtPointForSort', 'color: #795548', {
            point,
            siblingsCount: siblingElements.length,
            orientation: this._config.orientation
        });
        if (siblingElements.length === 0) {
            console.log('%c[DropListRef] _getItemIndexAtPointForSort - no siblings, returning 0', 'color: #795548');
            return 0;
        }
        const rects = siblingElements.map((el) => el.getBoundingClientRect());
        const orientation = this._config.orientation;
        let index;
        if (orientation === 'horizontal') {
            index = getHorizontalDropIndex(rects, point);
        }
        else if (orientation === 'vertical') {
            index = getVerticalDropIndex(rects, point);
        }
        else {
            index = getMixedDropIndex(rects, point);
        }
        // Clamp to valid range
        const result = Math.max(0, Math.min(index, this._items.length - 1));
        console.log('%c[DropListRef] _getItemIndexAtPointForSort - result', 'color: #795548', {
            rawIndex: index,
            clampedIndex: result,
            maxIndex: this._items.length - 1
        });
        return result;
    }
    /**
     * Get sibling elements in this container, optionally excluding a drag item.
     *
     * @param excludeDragItem - Drag item to exclude (uses its original element).
     * @returns Array of sibling elements.
     *
     * @private
     */
    _getSiblingElements(excludeDragItem) {
        const container = this._getItemContainer();
        const children = Array.from(container.children);
        // Filter to only include items from our items list (by matching elements or placeholders)
        return children.filter((child) => {
            // Exclude the dragged item's original element
            if (excludeDragItem && child === excludeDragItem.element) {
                return false;
            }
            // Include if it's a placeholder
            if (child.classList.contains('drag-placeholder')) {
                return true;
            }
            // Include if it's one of our items
            return this._items.some((item) => item.element === child);
        });
    }
    /**
     * Get the container element where items are placed.
     *
     * @returns Container element.
     *
     * @private
     */
    _getItemContainer() {
        if (this._config.elementContainerSelector) {
            const container = this._element.querySelector(this._config.elementContainerSelector);
            return container ?? this._element;
        }
        return this._element;
    }
    /**
     * Insert placeholder at a specific index in the DOM.
     *
     * @param placeholder - Placeholder element.
     * @param index - Target index.
     *
     * @private
     */
    _insertPlaceholderAtIndex(placeholder, index) {
        console.log('%c[DropListRef] _insertPlaceholderAtIndex', 'color: #607D8B', {
            id: this._config.id,
            targetIndex: index
        });
        const container = this._getItemContainer();
        const siblingElements = this._getSiblingElements(null);
        console.log('%c[DropListRef] _insertPlaceholderAtIndex - siblings', 'color: #607D8B', {
            siblingsCount: siblingElements.length,
            containerTag: container.tagName
        });
        if (index >= siblingElements.length) {
            // Append at the end
            container.appendChild(placeholder);
            console.log('%c[DropListRef] _insertPlaceholderAtIndex - appended at end', 'color: #607D8B');
        }
        else {
            // Insert before the element at the target index
            const referenceElement = siblingElements[index];
            container.insertBefore(placeholder, referenceElement);
            console.log('%c[DropListRef] _insertPlaceholderAtIndex - inserted before index', 'color: #607D8B', { index });
        }
    }
    /**
     * Move placeholder to a specific index in the DOM.
     *
     * @param placeholder - Placeholder element.
     * @param newIndex - Target index.
     * @param dragItem - The drag item (to exclude its original element).
     *
     * @private
     */
    _movePlaceholderToIndex(placeholder, newIndex, dragItem) {
        console.log('%c[DropListRef] _movePlaceholderToIndex', 'color: #3F51B5', {
            id: this._config.id,
            newIndex
        });
        const container = this._getItemContainer();
        // Get siblings excluding both the placeholder and the drag item's original element
        const children = Array.from(container.children);
        const siblings = children.filter((child) => {
            if (child === placeholder) {
                return false;
            }
            if (child === dragItem.element) {
                return false;
            }
            // Only include items that are part of this list
            if (child.classList.contains('drag-placeholder')) {
                return false;
            }
            return this._items.some((item) => item.element === child);
        });
        console.log('%c[DropListRef] _movePlaceholderToIndex - siblings', 'color: #3F51B5', {
            siblingsCount: siblings.length,
            childrenCount: children.length
        });
        if (newIndex >= siblings.length) {
            // Append at the end
            container.appendChild(placeholder);
            console.log('%c[DropListRef] _movePlaceholderToIndex - appended at end', 'color: #3F51B5');
        }
        else {
            // Insert before the sibling at the target index
            const referenceElement = siblings[newIndex];
            container.insertBefore(placeholder, referenceElement);
            console.log('%c[DropListRef] _movePlaceholderToIndex - inserted before', 'color: #3F51B5', { newIndex });
        }
    }
    /**
     * Get all items in their current sorted order.
     *
     * @returns Array of items.
     *
     * @public
     */
    getSortedItems() {
        return [...this._items];
    }
    /**
     * Get the index of a specific drag item.
     *
     * @param item - Drag item to find.
     * @returns Index of item, or -1 if not found.
     *
     * @public
     */
    getItemIndex(item) {
        return this._items.indexOf(item);
    }
    /**
     * Get the drag item at a specific index.
     *
     * @param index - Index to get item from.
     * @returns Drag item at index, or null if index is out of bounds.
     *
     * @public
     */
    getItemAtIndex(index) {
        return this._items[index] ?? null;
    }
    // #endregion
    // #region Private Methods - Events
    /**
     * Emit sort event.
     *
     * @param item - Item being sorted.
     * @param previousIndex - Previous index.
     * @param currentIndex - Current index.
     * @param pointerPosition - Pointer position.
     *
     * @private
     */
    _emitSortEvent(item, previousIndex, currentIndex, pointerPosition) {
        console.log('%c[DropListRef] _emitSortEvent', 'color: #00BCD4; font-weight: bold', {
            id: this._config.id,
            previousIndex,
            currentIndex
        });
        const detail = {
            type: 'sorted',
            previousIndex,
            currentIndex,
            item,
            container: this
        };
        const event = new CustomEvent('sort', {
            bubbles: true,
            composed: true,
            cancelable: false,
            detail
        });
        this._element.dispatchEvent(event);
        console.log('%c[DropListRef] _emitSortEvent - dispatched', 'color: #00BCD4');
    }
    // #endregion
    // #region Private Methods - Config
    /**
     * Apply default configuration.
     *
     * @param config - User config.
     * @returns Full config.
     *
     * @private
     */
    _applyConfigDefaults(config) {
        return {
            id: config.id ?? `drop-list-${Math.random().toString(36)
                .substr(2, 9)}`,
            disabled: config.disabled ?? false,
            orientation: config.orientation ?? 'vertical',
            enterPredicate: config.enterPredicate,
            sortPredicate: config.sortPredicate,
            sortingDisabled: config.sortingDisabled ?? false,
            autoScrollDisabled: config.autoScrollDisabled ?? false,
            autoScrollStep: config.autoScrollStep ?? 2,
            lockAxis: config.lockAxis ?? null,
            elementContainerSelector: config.elementContainerSelector ?? null,
            data: config.data
        };
    }
}
// #endregion
// #region Factory
/**
 * Create a new DropListRef instance.
 *
 * @param element - Element to make a drop container.
 * @param config - Configuration options.
 * @returns DropListRef instance.
 *
 * @public
 */
export function createDropListRef(element, config) {
    return new DropListRef(element, config);
}
// #endregion
//# sourceMappingURL=DropListRef.js.map