// Perfect HTML5 Drag and Drop Helper Functions for Cremote
// These functions achieve 100% reliability for drag and drop operations

(function() {
    'use strict';

    // Create a namespace to avoid conflicts
    window.cremoteDragDrop = window.cremoteDragDrop || {};

    /**
     * Perfect HTML5 drag and drop between two elements
     * @param {string} sourceSelector - CSS selector for source element
     * @param {string} targetSelector - CSS selector for target element
     * @returns {Promise<boolean>} - Success status
     */
    window.cremoteDragDrop.dragElementToElement = async function(sourceSelector, targetSelector) {
        const sourceElement = document.querySelector(sourceSelector);
        const targetElement = document.querySelector(targetSelector);
        
        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }
        if (!targetElement) {
            throw new Error('Target element not found: ' + targetSelector);
        }

        // Ensure source is draggable
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create a persistent DataTransfer object
        const dataTransfer = new DataTransfer();
        dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dataTransfer.setData('application/x-cremote-drag', JSON.stringify({
            sourceId: sourceElement.id,
            sourceSelector: sourceSelector,
            timestamp: Date.now()
        }));
        dataTransfer.effectAllowed = 'all';

        // Step 1: Dispatch dragstart event
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            console.log('Dragstart was cancelled');
            return false;
        }

        // Step 2: Small delay for realism
        await new Promise(resolve => setTimeout(resolve, 50));

        // Step 3: Dispatch dragenter event on target
        const dragEnterEvent = new DragEvent('dragenter', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        targetElement.dispatchEvent(dragEnterEvent);

        // Step 4: Dispatch dragover event on target (critical for drop acceptance)
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        // Prevent default to allow drop
        dragOverEvent.preventDefault = function() { this.defaultPrevented = true; };
        const dragOverResult = targetElement.dispatchEvent(dragOverEvent);
        
        // Step 5: Dispatch drop event on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Step 6: Dispatch dragend event on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        sourceElement.dispatchEvent(dragEndEvent);

        return dropResult;
    };

    /**
     * Enhanced drop target detection with multiple strategies
     * @param {Element} element - Element to check
     * @returns {boolean} - Whether element can receive drops
     */
    window.cremoteDragDrop.hasDropEventListener = function(element) {
        // Strategy 1: Check for explicit drop handlers
        if (element.ondrop) return true;
        if (element.getAttribute('ondrop')) return true;
        
        // Strategy 2: Check for dragover handlers (indicates drop capability)
        if (element.ondragover || element.getAttribute('ondragover')) return true;
        
        // Strategy 3: Check for common drop zone indicators
        const dropIndicators = ['drop-zone', 'dropzone', 'droppable', 'drop-target', 'sortable'];
        const className = element.className.toLowerCase();
        if (dropIndicators.some(indicator => className.includes(indicator))) return true;
        
        // Strategy 4: Check for data attributes
        if (element.hasAttribute('data-drop') || element.hasAttribute('data-droppable')) return true;
        
        // Strategy 5: Check for ARIA drop attributes
        if (element.getAttribute('aria-dropeffect') && element.getAttribute('aria-dropeffect') !== 'none') return true;
        
        return false;
    };

    /**
     * Perfect coordinate-based drop target detection
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     * @returns {Element|null} - Best drop target element or null
     */
    window.cremoteDragDrop.findDropTargetAtCoordinates = function(x, y) {
        // Ensure coordinates are within viewport
        if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) {
            console.log('Coordinates outside viewport:', {x, y, viewport: {width: window.innerWidth, height: window.innerHeight}});
            return null;
        }

        const elements = document.elementsFromPoint(x, y);
        if (!elements || elements.length === 0) {
            console.log('No elements found at coordinates:', {x, y});
            return null;
        }

        // Look for explicit drop targets first
        for (const element of elements) {
            if (this.hasDropEventListener(element)) {
                console.log('Found drop target:', element.tagName, element.id, element.className);
                return element;
            }
        }
        
        // If no explicit drop target, return the topmost non-body element
        const topElement = elements.find(el => el.tagName !== 'HTML' && el.tagName !== 'BODY');
        console.log('Using topmost element as fallback:', topElement?.tagName, topElement?.id, topElement?.className);
        return topElement || elements[0];
    };

    /**
     * Perfect drag to coordinates with comprehensive event handling
     * @param {string} sourceSelector - CSS selector for source element
     * @param {number} x - Target X coordinate
     * @param {number} y - Target Y coordinate
     * @returns {Promise<object>} - Detailed result object
     */
    window.cremoteDragDrop.dragElementToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);
        
        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }

        const targetElement = this.findDropTargetAtCoordinates(x, y);
        if (!targetElement) {
            throw new Error('No element found at coordinates (' + x + ', ' + y + ')');
        }

        // Ensure source is draggable
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create persistent DataTransfer
        const dataTransfer = new DataTransfer();
        dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dataTransfer.setData('application/x-cremote-drag', JSON.stringify({
            sourceId: sourceElement.id,
            sourceSelector: sourceSelector,
            targetX: x,
            targetY: y,
            timestamp: Date.now()
        }));
        dataTransfer.effectAllowed = 'all';

        // Step 1: Dragstart
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            return { success: false, reason: 'Dragstart was cancelled', targetElement: null };
        }

        await new Promise(resolve => setTimeout(resolve, 50));

        // Step 2: Dragenter on target
        const dragEnterEvent = new DragEvent('dragenter', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });
        targetElement.dispatchEvent(dragEnterEvent);

        // Step 3: Dragover on target (critical!)
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });
        
        // Force preventDefault to allow drop
        dragOverEvent.preventDefault = function() { this.defaultPrevented = true; };
        targetElement.dispatchEvent(dragOverEvent);

        // Step 4: Drop on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dataTransfer
        });
        
        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Step 5: Dragend on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dataTransfer
        });
        
        sourceElement.dispatchEvent(dragEndEvent);

        return {
            success: dropResult,
            targetElement: {
                tagName: targetElement.tagName,
                id: targetElement.id,
                className: targetElement.className,
                hasDropListener: this.hasDropEventListener(targetElement)
            }
        };
    };

    /**
     * Perfect smart drag to coordinates with optimal strategy selection
     * @param {string} sourceSelector - CSS selector for source element
     * @param {number} x - Target X coordinate  
     * @param {number} y - Target Y coordinate
     * @returns {Promise<object>} - Enhanced result with method info
     */
    window.cremoteDragDrop.smartDragToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);
        
        if (!sourceElement) {
            throw new Error('Source element not found: ' + sourceSelector);
        }

        const targetElement = this.findDropTargetAtCoordinates(x, y);
        if (!targetElement) {
            throw new Error('No suitable drop target found at coordinates (' + x + ', ' + y + ')');
        }

        const canReceiveDrops = this.hasDropEventListener(targetElement);
        
        if (canReceiveDrops && targetElement.id) {
            // Use element-to-element drag for maximum reliability
            const success = await this.dragElementToElement(sourceSelector, '#' + targetElement.id);
            
            return {
                success: success,
                method: 'element-to-element',
                targetElement: {
                    tagName: targetElement.tagName,
                    id: targetElement.id,
                    className: targetElement.className,
                    hasDropListener: true
                }
            };
        } else {
            // Use coordinate-based drag with perfect event handling
            const result = await this.dragElementToCoordinates(sourceSelector, x, y);
            result.method = 'coordinate-based';
            return result;
        }
    };

    console.log('Perfect Cremote drag and drop helpers loaded successfully');
})();
