/* eslint-disable @typescript-eslint/no-explicit-any */
// #region Imports
import { nothing } from 'lit';
import { directive, PartType } from 'lit/directive.js';
import { DirectiveBase } from '../../Controls/Directives/Abstracts/DirectiveBase';
import { createDragRef } from '../Core/DragRef';
/**
 * Lit Directive for making elements draggable.
 *
 * @internal
 */
class DragDirective extends DirectiveBase {
    // #region Fields
    _dragRef = null;
    _lastConfig = null;
    // #endregion
    // #region Ctor
    /**
     * @param args - Directive args.
     */
    constructor(args) {
        super(args);
        // Validate part type
        if (args.type !== PartType.ELEMENT) {
            throw new Error('drag() directive can only be used on elements');
        }
    }
    // #endregion
    // #region Methods
    /**
     * Render method.
     *
     * @param config - Configuration.
     */
    render(_config) {
        return nothing;
    }
    /**
     * Update method.
     *
     * @param part - Element part.
     * @param config - Configuration.
     */
    update(part, [config]) {
        const element = part.element;
        element.setAttribute('data-drag', '');
        // Create dragRef on first update
        if (!this._dragRef) {
            console.log('%c[DragDirective] Creating dragRef', 'color: #FF5722; font-weight: bold', {
                elementTag: element.tagName,
                config
            });
            this._dragRef = createDragRef(element, config);
            if (config?.data !== undefined) {
                this._dragRef.data = config.data;
            }
            this._lastConfig = config ?? null;
            // Expose dragRef on element for dropList auto-discovery
            element.__dragRef = this._dragRef;
            // Auto-discover and register drag handles, preview, and placeholder (delayed to next microtask)
            // This ensures child directives (dragHandle, dragPreview, dragPlaceholder) have been processed
            Promise.resolve().then(() => {
                this._discoverAndRegisterHandles(element);
                this._discoverAndRegisterPreview(element);
                this._discoverAndRegisterPlaceholder(element);
            });
            // Try to find parent drop list and register with it
            this._registerWithParentDropList(element);
        }
        else if (this._configChanged(config)) {
            // Update config if changed
            const { data, ...refConfig } = config ?? {};
            Object.assign(this._dragRef, refConfig);
            if (data !== undefined) {
                this._dragRef.data = data;
            }
            this._lastConfig = config ?? null;
        }
    }
    // #endregion
    // #region Private Methods
    /**
     * Try to find a parent drop list and register with it.
     *
     * @param element - The drag element.
     *
     * @private
     */
    _registerWithParentDropList(element) {
        if (!this._dragRef) {
            return;
        }
        // Walk up the DOM to find a parent with __dropListRef
        let parent = element.parentElement;
        while (parent) {
            const dropListRef = parent.__dropListRef;
            if (dropListRef) {
                console.log('%c[DragDirective] Found parent dropList, registering', 'color: #4CAF50');
                this._dragRef.withDropContainer(dropListRef);
                break;
            }
            parent = parent.parentElement;
        }
    }
    /**
     * Discover and register drag handles within the element.
     *
     * @param element - The drag element.
     *
     * @private
     */
    _discoverAndRegisterHandles(element) {
        if (!this._dragRef) {
            return;
        }
        // Find all elements with data-drag-handle attribute
        const handleElements = Array.from(element.querySelectorAll('[data-drag-handle]'));
        if (handleElements.length > 0) {
            console.log('%c[DragDirective] Found drag handles, registering', 'color: #9C27B0', {
                count: handleElements.length
            });
            this._dragRef.withHandles(handleElements);
        }
        else {
            console.log('%c[DragDirective] No drag handles found, using entire element', 'color: #9C27B0');
        }
    }
    /**
     * Discover and register custom preview template within the element.
     *
     * @param element - The drag element.
     *
     * @private
     */
    _discoverAndRegisterPreview(element) {
        if (!this._dragRef) {
            return;
        }
        // Find element with data-drag-preview attribute
        const previewElement = element.querySelector('[data-drag-preview]');
        if (previewElement) {
            console.log('%c[DragDirective] Found drag preview template, registering', 'color: #9C27B0', {
                element: previewElement,
                matchSize: previewElement.getAttribute('data-preview-match-size') === 'true'
            });
            this._dragRef.withPreviewTemplate(previewElement);
        }
    }
    /**
     * Discover and register custom placeholder template within the element.
     *
     * @param element - The drag element.
     *
     * @private
     */
    _discoverAndRegisterPlaceholder(element) {
        if (!this._dragRef) {
            return;
        }
        // Find element with data-drag-placeholder attribute
        const placeholderElement = element.querySelector('[data-drag-placeholder]');
        if (placeholderElement) {
            console.log('%c[DragDirective] Found drag placeholder template, registering', 'color: #9C27B0', {
                element: placeholderElement
            });
            this._dragRef.withPlaceholderTemplate(placeholderElement);
        }
    }
    /**
     * 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)
        const keys = Object.keys(newConfig).filter((k) => k !== 'data');
        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 drag ref.
     *
     * @public
     */
    disconnected() {
        console.log('%c[DragDirective] Disconnected, cleaning up', 'color: #FF9800');
        if (this._dragRef) {
            this._dragRef.dispose();
            this._dragRef = null;
        }
    }
    /**
     * Called when the directive is reconnected to the DOM.
     *
     * @public
     */
    reconnected() {
        console.log('%c[DragDirective] Reconnected', 'color: #4CAF50');
        // Drag ref will be recreated in next update
    }
}
// #endregion
/**
 * Make an element draggable.
 *
 * @param config - Configuration options.
 * @returns Directive.
 *
 * @public
 *
 * @example
 * ```html
 * <div ${drag({ lockAxis: 'x' })}>Drag me</div>
 * ```
 */
export const drag = directive(DragDirective);
//# sourceMappingURL=DragDirective.js.map