// HTML5 Drag and Drop Helper Functions for Cremote
// These functions are injected into web pages to provide reliable drag and drop functionality

(function() {
    'use strict';

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

    /**
     * Simulates 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}`);
        }

        // Make source draggable if not already
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create and dispatch dragstart event
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: new DataTransfer()
        });
        
        // Set drag data
        dragStartEvent.dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dragStartEvent.dataTransfer.effectAllowed = 'all';
        
        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            console.log('Dragstart was cancelled');
            return false;
        }

        // Small delay to simulate realistic drag timing
        await new Promise(resolve => setTimeout(resolve, 50));

        // Create and dispatch dragover event on target
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        const dragOverResult = targetElement.dispatchEvent(dragOverEvent);
        
        // Create and dispatch drop event on target
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Create and dispatch dragend event on source
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        sourceElement.dispatchEvent(dragEndEvent);

        return dropResult;
    };

    /**
     * Simulates HTML5 drag and drop from element to coordinates
     * @param {string} sourceSelector - CSS selector for source element
     * @param {number} x - Target X coordinate
     * @param {number} y - Target Y coordinate
     * @returns {Promise<object>} - Result with success status and target element info
     */
    window.cremoteDragDrop.dragElementToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);
        
        if (!sourceElement) {
            throw new Error(`Source element not found: ${sourceSelector}`);
        }

        // Find element at target coordinates
        const targetElement = document.elementFromPoint(x, y);
        if (!targetElement) {
            throw new Error(`No element found at coordinates (${x}, ${y})`);
        }

        // Make source draggable if not already
        if (!sourceElement.draggable) {
            sourceElement.draggable = true;
        }

        // Create and dispatch dragstart event
        const dragStartEvent = new DragEvent('dragstart', {
            bubbles: true,
            cancelable: true,
            dataTransfer: new DataTransfer()
        });
        
        dragStartEvent.dataTransfer.setData('text/plain', sourceElement.id || sourceSelector);
        dragStartEvent.dataTransfer.effectAllowed = 'all';
        
        const dragStartResult = sourceElement.dispatchEvent(dragStartEvent);
        if (!dragStartResult) {
            return { success: false, reason: 'Dragstart was cancelled', targetElement: null };
        }

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

        // Create dragover event with coordinates
        const dragOverEvent = new DragEvent('dragover', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        targetElement.dispatchEvent(dragOverEvent);

        // Create drop event with coordinates
        const dropEvent = new DragEvent('drop', {
            bubbles: true,
            cancelable: true,
            clientX: x,
            clientY: y,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        const dropResult = targetElement.dispatchEvent(dropEvent);

        // Dispatch dragend
        const dragEndEvent = new DragEvent('dragend', {
            bubbles: true,
            cancelable: true,
            dataTransfer: dragStartEvent.dataTransfer
        });
        
        sourceElement.dispatchEvent(dragEndEvent);

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

    /**
     * Checks if an element can receive drop events
     * @param {Element} element - Element to check
     * @returns {boolean} - Whether element can receive drops
     */
    window.cremoteDragDrop.hasDropEventListener = function(element) {
        // Check for ondrop attribute
        if (element.ondrop) return true;
        
        // Check for drop event listeners (this is limited but covers common cases)
        if (element.getAttribute('ondrop')) return true;
        
        // Check if element has dragover listeners (usually indicates drop capability)
        if (element.ondragover || element.getAttribute('ondragover')) return true;
        
        // Check for common drop zone classes/attributes
        const dropIndicators = ['drop-zone', 'dropzone', 'droppable', 'drop-target'];
        const className = element.className.toLowerCase();
        const hasDropClass = dropIndicators.some(indicator => className.includes(indicator));
        
        return hasDropClass;
    };

    /**
     * Finds the best drop target at given coordinates
     * @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) {
        const elements = document.elementsFromPoint(x, y);
        
        // Look for elements that can receive drops, starting from topmost
        for (const element of elements) {
            if (this.hasDropEventListener(element)) {
                return element;
            }
        }
        
        // If no explicit drop target found, return the topmost element
        return elements[0] || null;
    };

    /**
     * Enhanced drag to coordinates that finds the best drop target
     * @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 target analysis
     */
    window.cremoteDragDrop.smartDragToCoordinates = async function(sourceSelector, x, y) {
        const sourceElement = document.querySelector(sourceSelector);
        
        if (!sourceElement) {
            throw new Error(`Source element not found: ${sourceSelector}`);
        }

        // Find the best drop target at coordinates
        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 we found a proper drop target, use element-to-element drag
        if (canReceiveDrops) {
            const success = await this.dragElementToElement(sourceSelector, 
                targetElement.id ? `#${targetElement.id}` : targetElement.tagName.toLowerCase());
            
            return {
                success: success,
                method: 'element-to-element',
                targetElement: {
                    tagName: targetElement.tagName,
                    id: targetElement.id,
                    className: targetElement.className,
                    hasDropListener: true
                }
            };
        } else {
            // Fall back to coordinate-based drag
            const result = await this.dragElementToCoordinates(sourceSelector, x, y);
            result.method = 'coordinate-based';
            return result;
        }
    };

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