/* eslint-disable @typescript-eslint/no-explicit-any */
// #region Imports
import { directive } from 'lit/directive.js';
import { DirectiveBase, PartType } from '../../Controls/Directives/Abstracts/DirectiveBase';
import { createDropListRef } from '../Core/DropListRef';
/**
 * Lit Directive for making elements drop containers.
 *
 * @internal
 */
class DropListDirective extends DirectiveBase {
    // #region Fields
    _dropListRef = null;
    _lastConfig = null;
    _element = null;
    _groupRef = null;
    _mutationObserver = null;
    // #endregion
    // #region Ctor
    /**
     * @param args - Directive args.
     */
    constructor(args) {
        super(args);
        // Validate part type
        if (args.type !== PartType.ELEMENT) {
            throw new Error('dropList() directive can only be used on elements');
        }
    }
    // #endregion
    // #region Methods
    /**
     * Render method.
     *
     * @private
     */
    render(_config) {
        // Render returns nothing - all work is done in update
        return undefined;
    }
    update(part, [config]) {
        const element = part.element;
        this._element = element;
        element.setAttribute('data-drop-list', '');
        // Create dropListRef on first update
        if (!this._dropListRef) {
            this._dropListRef = createDropListRef(element, config);
            this._lastConfig = config ?? null;
            // Register with parent group if available
            this._registerWithGroup(element);
            // Setup auto-discovery of drag items
            if (config?.autoDiscoverItems !== false) {
                this._setupAutoDiscovery(element);
            }
            // Expose dropListRef on element for drag directives to find
            element.__dropListRef = this._dropListRef;
        }
        else if (this._configChanged(config)) {
            // Update config if changed
            const mergedConfig = {
                ...this._lastConfig,
                ...config
            };
            Object.assign(this._dropListRef, mergedConfig);
            this._lastConfig = config ?? null;
        }
        // Update data if provided
        if (config?.data !== undefined) {
            this._dropListRef.data = config.data;
        }
        // Update items if provided explicitly
        if (config?.items) {
            this._dropListRef.withItems(config.items);
        }
        else if (config?.autoDiscoverItems !== false) {
            // Auto-discover items
            this._discoverAndRegisterItems(element);
        }
        // Update connected lists if provided
        if (config?.connectedTo) {
            this._dropListRef.connectTo(config.connectedTo);
        }
        return this.render(config);
    }
    // #endregion
    // #region Private Methods
    /**
     * Register this drop list with a parent group ref if available.
     *
     * @param element - The element.
     *
     * @private
     */
    _registerWithGroup(element) {
        // Walk up the DOM to find a parent with __dropListGroupRef
        let parent = element.parentElement;
        while (parent) {
            const groupRef = parent.__dropListGroupRef;
            if (groupRef && this._dropListRef) {
                groupRef.registerDropList(this._dropListRef);
                this._groupRef = groupRef;
                break;
            }
            parent = parent.parentElement;
        }
    }
    /**
     * Setup mutation observer to auto-discover drag items.
     *
     * @param element - The container element.
     *
     * @private
     */
    _setupAutoDiscovery(element) {
        // Use MutationObserver to detect when child elements with drag are added
        this._mutationObserver = new MutationObserver(() => {
            this._discoverAndRegisterItems(element);
        });
        this._mutationObserver.observe(element, {
            childList: true,
            subtree: true
        });
        // Initial discovery after a microtask (to ensure drag directives have run)
        queueMicrotask(() => {
            this._discoverAndRegisterItems(element);
        });
    }
    /**
     * Discover child drag items and register them with the drop list.
     *
     * @param element - The container element.
     *
     * @private
     */
    _discoverAndRegisterItems(element) {
        if (!this._dropListRef) {
            return;
        }
        // Skip re-discovery while a drag operation is active.
        // DOM mutations from placeholder movement would otherwise
        // overwrite the sorted _items array with the original DOM order.
        if (this._dropListRef.isDragging()) {
            return;
        }
        // Find all child elements with __dragRef property
        const dragItems = [];
        const walkChildren = (el) => {
            for (const child of Array.from(el.children)) {
                const dragRef = child.__dragRef;
                if (dragRef) {
                    dragItems.push(dragRef);
                }
                else {
                    // Don't recurse into nested drop lists
                    if (!child.__dropListRef) {
                        walkChildren(child);
                    }
                }
            }
        };
        walkChildren(element);
        if (dragItems.length > 0) {
            this._dropListRef.withItems(dragItems);
        }
    }
    /**
     * Check if config changed.
     *
     * @param newConfig - New config.
     * @returns True if changed.
     *
     * @private
     */
    _configChanged(newConfig) {
        if (!this._lastConfig && !newConfig) {
            return false;
        }
        if (!this._lastConfig || !newConfig) {
            return true;
        }
        // Shallow comparison (excluding data, items, connectedTo)
        const keys = Object.keys(newConfig).filter((k) => k !== 'data' && k !== 'items' && k !== 'connectedTo');
        return keys.some((key) => {
            const k = key;
            return this._lastConfig[k] !== newConfig[k];
        });
    }
    // #endregion
    // #region Lifecycle
    /**
     * Called when the directive is disconnected from the DOM.
     * Cleans up the mutation observer and drop list ref.
     *
     * @public
     */
    disconnected() {
        // Stop mutation observer
        if (this._mutationObserver) {
            this._mutationObserver.disconnect();
            this._mutationObserver = null;
        }
        // Unregister from group ref
        if (this._groupRef && this._dropListRef) {
            this._groupRef.unregisterDropList(this._dropListRef);
            this._groupRef = null;
        }
        // Dispose drop list ref
        if (this._dropListRef) {
            this._dropListRef.dispose();
            this._dropListRef = null;
        }
    }
    /**
     * Called when the directive is reconnected to the DOM.
     *
     * @public
     */
    reconnected() {
        // Controller will be recreated in next update
    }
}
// #endregion
/**
 * Make an element a drop container.
 *
 * @param config - Configuration options.
 * @returns Directive.
 *
 * @public
 *
 * @example
 * ```html
 * <div ${dropList({ orientation: 'horizontal' })}>
 *   <!-- Draggable items -->
 * </div>
 * ```
 */
export const dropList = directive(DropListDirective);
//# sourceMappingURL=DropListDirective.js.map