/**
 * Lazy Component Loader
 *
 * Loads LiveComponents only when they enter the viewport using Intersection Observer API.
 * Provides performance optimization for pages with many components.
 *
 * Features:
 * - Viewport-based loading with configurable thresholds
 * - Placeholder management during loading
 * - Progressive loading with priorities
 * - Memory-efficient intersection tracking
 * - Automatic cleanup and disconnection
 *
 * Usage:
 * <div data-live-component-lazy="notification-center:user-123"
 *      data-lazy-threshold="0.1"
 *      data-lazy-priority="high"
 *      data-lazy-placeholder="Loading notifications...">
 * </div>
 */

export class LazyComponentLoader {
    constructor(liveComponentManager) {
        this.liveComponentManager = liveComponentManager;
        this.observer = null;
        this.lazyComponents = new Map(); // element → config
        this.loadingQueue = []; // Priority-based loading queue
        this.isProcessingQueue = false;

        this.defaultOptions = {
            rootMargin: '50px', // Load 50px before entering viewport
            threshold: 0.1 // Trigger when 10% visible
        };
    }

    /**
     * Initialize lazy loading system
     */
    init() {
        // Create Intersection Observer
        this.observer = new IntersectionObserver(
            (entries) => this.handleIntersection(entries),
            this.defaultOptions
        );

        // Scan for lazy components
        this.scanLazyComponents();

        console.log(`[LazyLoader] Initialized with ${this.lazyComponents.size} lazy components`);
    }

    /**
     * Scan DOM for lazy components
     */
    scanLazyComponents() {
        const lazyElements = document.querySelectorAll('[data-live-component-lazy]');

        lazyElements.forEach(element => {
            this.registerLazyComponent(element);
        });
    }

    /**
     * Register a lazy component for loading
     *
     * @param {HTMLElement} element - Component container
     */
    registerLazyComponent(element) {
        const componentId = element.dataset.liveComponentLazy;
        if (!componentId) {
            console.warn('[LazyLoader] Lazy component missing componentId:', element);
            return;
        }

        // Extract configuration
        const config = {
            element,
            componentId,
            threshold: parseFloat(element.dataset.lazyThreshold) || this.defaultOptions.threshold,
            priority: element.dataset.lazyPriority || 'normal', // high, normal, low
            placeholder: element.dataset.lazyPlaceholder || null,
            loaded: false,
            loading: false
        };

        // Show placeholder
        if (config.placeholder) {
            this.showPlaceholder(element, config.placeholder);
        }

        // Store config
        this.lazyComponents.set(element, config);

        // Start observing
        this.observer.observe(element);

        console.log(`[LazyLoader] Registered lazy component: ${componentId}`);
    }

    /**
     * Handle intersection observer callback
     *
     * @param {Array<IntersectionObserverEntry>} entries - Intersection entries
     */
    handleIntersection(entries) {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const config = this.lazyComponents.get(entry.target);
                if (config && !config.loaded && !config.loading) {
                    this.queueComponentLoad(config);
                }
            }
        });
    }

    /**
     * Queue component for loading with priority
     *
     * @param {Object} config - Component config
     */
    queueComponentLoad(config) {
        const priorityWeight = this.getPriorityWeight(config.priority);

        this.loadingQueue.push({
            config,
            priority: priorityWeight,
            timestamp: Date.now()
        });

        // Sort by priority (high to low) and then by timestamp (early to late)
        this.loadingQueue.sort((a, b) => {
            if (b.priority !== a.priority) {
                return b.priority - a.priority;
            }
            return a.timestamp - b.timestamp;
        });

        console.log(`[LazyLoader] Queued: ${config.componentId} (priority: ${config.priority})`);

        // Process queue
        this.processLoadingQueue();
    }

    /**
     * Get numeric weight for priority
     *
     * @param {string} priority - Priority level (high, normal, low)
     * @returns {number} Priority weight
     */
    getPriorityWeight(priority) {
        const weights = {
            'high': 3,
            'normal': 2,
            'low': 1
        };
        return weights[priority] || 2;
    }

    /**
     * Process loading queue
     *
     * Loads components sequentially to avoid overloading server.
     */
    async processLoadingQueue() {
        if (this.isProcessingQueue || this.loadingQueue.length === 0) {
            return;
        }

        this.isProcessingQueue = true;

        while (this.loadingQueue.length > 0) {
            const { config } = this.loadingQueue.shift();

            if (!config.loaded && !config.loading) {
                await this.loadComponent(config);
            }
        }

        this.isProcessingQueue = false;
    }

    /**
     * Load component from server
     *
     * @param {Object} config - Component config
     */
    async loadComponent(config) {
        config.loading = true;

        try {
            console.log(`[LazyLoader] Loading: ${config.componentId}`);

            // Show loading indicator
            this.showLoadingIndicator(config.element);

            // Request component HTML from server
            const response = await fetch(`/live-component/${config.componentId}/lazy-load`, {
                method: 'GET',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }

            const data = await response.json();

            if (!data.success) {
                throw new Error(data.error || 'Failed to load component');
            }

            // Replace placeholder with component HTML
            config.element.innerHTML = data.html;

            // Mark element as regular LiveComponent (no longer lazy)
            config.element.setAttribute('data-live-component', config.componentId);
            config.element.removeAttribute('data-live-component-lazy');

            // Copy CSRF token to element
            if (data.csrf_token) {
                config.element.dataset.csrfToken = data.csrf_token;
            }

            // Copy state to element
            if (data.state) {
                config.element.dataset.state = JSON.stringify(data.state);
            }

            // Initialize as regular LiveComponent
            this.liveComponentManager.init(config.element);

            // Mark as loaded
            config.loaded = true;
            config.loading = false;

            // Stop observing
            this.observer.unobserve(config.element);

            console.log(`[LazyLoader] Loaded: ${config.componentId}`);

            // Dispatch custom event
            config.element.dispatchEvent(new CustomEvent('livecomponent:lazy:loaded', {
                detail: { componentId: config.componentId }
            }));

        } catch (error) {
            console.error(`[LazyLoader] Failed to load ${config.componentId}:`, error);

            config.loading = false;

            // Show error state
            this.showError(config.element, error.message);

            // Dispatch error event
            config.element.dispatchEvent(new CustomEvent('livecomponent:lazy:error', {
                detail: {
                    componentId: config.componentId,
                    error: error.message
                }
            }));
        }
    }

    /**
     * Show placeholder
     *
     * @param {HTMLElement} element - Container element
     * @param {string} text - Placeholder text
     */
    showPlaceholder(element, text) {
        element.innerHTML = `
            <div class="livecomponent-lazy-placeholder" style="
                padding: 2rem;
                text-align: center;
                color: #666;
                background: #f5f5f5;
                border-radius: 8px;
                border: 1px dashed #ddd;
            ">
                <div style="font-size: 1.5rem; margin-bottom: 0.5rem;">⏳</div>
                <div>${text}</div>
            </div>
        `;
    }

    /**
     * Show loading indicator
     *
     * @param {HTMLElement} element - Container element
     */
    showLoadingIndicator(element) {
        element.innerHTML = `
            <div class="livecomponent-lazy-loading" style="
                padding: 2rem;
                text-align: center;
                color: #2196F3;
                background: #f5f5f5;
                border-radius: 8px;
                border: 1px solid #e3f2fd;
            ">
                <div class="spinner" style="
                    width: 40px;
                    height: 40px;
                    margin: 0 auto 1rem;
                    border: 4px solid #e3f2fd;
                    border-top: 4px solid #2196F3;
                    border-radius: 50%;
                    animation: spin 1s linear infinite;
                "></div>
                <div>Loading component...</div>
            </div>
            <style>
                @keyframes spin {
                    0% { transform: rotate(0deg); }
                    100% { transform: rotate(360deg); }
                }
            </style>
        `;
    }

    /**
     * Show error state
     *
     * @param {HTMLElement} element - Container element
     * @param {string} errorMessage - Error message
     */
    showError(element, errorMessage) {
        element.innerHTML = `
            <div class="livecomponent-lazy-error" style="
                padding: 2rem;
                text-align: center;
                color: #d32f2f;
                background: #ffebee;
                border-radius: 8px;
                border: 1px solid #ef9a9a;
            ">
                <div style="font-size: 1.5rem; margin-bottom: 0.5rem;">❌</div>
                <div><strong>Failed to load component</strong></div>
                <div style="margin-top: 0.5rem; font-size: 0.9rem; color: #c62828;">
                    ${errorMessage}
                </div>
            </div>
        `;
    }

    /**
     * Unregister lazy component and stop observing
     *
     * @param {HTMLElement} element - Component element
     */
    unregister(element) {
        const config = this.lazyComponents.get(element);
        if (!config) return;

        // Stop observing
        this.observer.unobserve(element);

        // Remove from registry
        this.lazyComponents.delete(element);

        console.log(`[LazyLoader] Unregistered: ${config.componentId}`);
    }

    /**
     * Destroy lazy loader
     *
     * Clean up all observers and references.
     */
    destroy() {
        // Disconnect observer
        if (this.observer) {
            this.observer.disconnect();
            this.observer = null;
        }

        // Clear queue
        this.loadingQueue = [];

        // Clear registry
        this.lazyComponents.clear();

        console.log('[LazyLoader] Destroyed');
    }

    /**
     * Get lazy loading statistics
     *
     * @returns {Object} Statistics
     */
    getStats() {
        let loaded = 0;
        let loading = 0;
        let pending = 0;

        this.lazyComponents.forEach(config => {
            if (config.loaded) loaded++;
            else if (config.loading) loading++;
            else pending++;
        });

        return {
            total: this.lazyComponents.size,
            loaded,
            loading,
            pending,
            queued: this.loadingQueue.length
        };
    }
}

// Export for use in LiveComponent module
export default LazyComponentLoader;
