/**
 * Module Loader System
 * 
 * Handles loading and initializing modules in the correct order,
 * with dependency management and progress reporting.
 */
import { moduleRegistry } from './module-registry.js';
import { ModuleEvent } from './base-module.js';

/**
 * Module States
 */
const ModuleState = {
    PENDING: 'PENDING',
    LOADING: 'LOADING',
    WAITING: 'WAITING',
    INITIALIZING: 'INITIALIZING',
    FINISHED: 'FINISHED',
    ERROR: 'ERROR'
};

/**
 * Module Loader - Manages the loading of all modules
 */
const ModuleLoader = (function() {
    // Private variables
    let loadingOverlay = null;
    let modulesList = null;
    let progressIndicator = null;
    let progressText = null;
    let statusText = null;
    let isLoadingComplete = false;
    let moduleWeights = {};
    let createdModules = new Set(); // Track which modules we've created UI elements for
    let gameLoopModule = null; // Add variable to hold game loop instance

    /**
     * Initialize the loader
     */
    function init() {
        // Prevent duplicate initialization
        if (createdModules.size > 0) {
            console.warn('Module Loader already initialized');
            return;
        }
        
        console.log('Module Loader: Initialization started');
        // Create the loading overlay
        createLoadingOverlay();
        
        // Setup event listeners
        setupEventListeners();
        
        // Load available module scripts
        loadModuleScripts().then(() => {
            // Once scripts are loaded, initialize modules
            initializeModules();
        });
    }

    /**
     * Setup event listeners for module communication
     */
    function setupEventListeners() {
        // Listen for module progress events
        document.addEventListener('module:progress', handleModuleProgress);
        
        // Listen for module state change events
        document.addEventListener('module:stateChange', handleModuleStateChange);
        
        // Listen for module status message events
        document.addEventListener('module:message', handleModuleMessage);
    }

    /**
     * Load all module scripts
     * @returns {Promise} - Resolves when all module scripts are loaded
     */
    async function loadModuleScripts() {
        // Define modules with their weights
        const modulesToLoad = [
            // Core functionality modules
            { id: 'persistence-manager', script: '/js/persistence-manager.js', weight: 40 },
            { id: 'localization', script: '/js/localization.js', weight: 40 },
            { id: 'text-processor', script: '/js/text-processor.js', weight: 40 },
            { id: 'paragraph-layout', script: '/js/paragraph-layout.js', weight: 40 },
            { id: 'animation-queue', script: '/js/animation-queue.js', weight: 50 },
            
            // Audio and TTS modules
            { id: 'audio-manager', script: '/js/audio-manager.js', weight: 60 },
            { id: 'tts', script: '/js/tts-player.js', weight: 75 },
            
            // UI and interaction modules
            { id: 'text-buffer', script: '/js/text-buffer.js', weight: 50 },
            { id: 'ui-effects', script: '/js/ui-effects.js', weight: 50 },        // Add UI Effects module
            { id: 'ui-input-handler', script: '/js/ui-input-handler.js', weight: 50 }, // Add UI Input Handler module
            { id: 'ui-display-handler', script: '/js/ui-display-handler.js', weight: 60 }, // Add UI Display Handler module
            { id: 'ui-controller', script: '/js/ui-controller.js', weight: 100 },
            { id: 'options-ui', script: '/js/options-ui.js', weight: 40 },
            { id: 'socket-client', script: '/js/socket-client.js', weight: 60 },
            
            // Main game module - should be last to load
            { id: 'game-loop', script: '/js/game-loop.js', weight: 25 }
        ];
        
        // Store module weights for progress calculation
        modulesToLoad.forEach(module => {
            moduleWeights[module.id] = module.weight;
        });
        
        // Create a module list entry for each module
        modulesToLoad.forEach(module => {
            createModuleListItem(module.id, getModuleNameFromId(module.id));
        });
        
        // Load each module script
        const loadPromises = modulesToLoad.map(module => loadScript(module.script));
        return Promise.all(loadPromises);
    }
    
    /**
     * Load a script dynamically
     * @param {string} src - Script source URL
     * @returns {Promise} - Resolves when script is loaded
     */
    function loadScript(src) {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.type = 'module';
            script.src = src;
            script.onload = () => resolve();
            script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
            document.head.appendChild(script);
        });
    }
    
    /**
     * Initialize all registered modules
     */
    function initializeModules() {
        const modules = moduleRegistry.getAllModules();

        // Find the game loop module instance
        gameLoopModule = moduleRegistry.getModule('game-loop');

        // For each registered module, start initialization
        Object.values(modules).forEach(async (module) => {
            try {
                // Create a progress callback for this module
                const progressCallback = (percent, message) => {
                    handleModuleProgress({
                        detail: { 
                            moduleId: module.id, 
                            progress: percent 
                        }
                    });
                    
                    if (message) {
                        handleModuleMessage({
                            detail: { 
                                moduleId: module.id, 
                                message 
                            }
                        });
                    }
                };
                
                // Initialize the module with progress callback
                await module.initializeInterface(progressCallback);
                
            } catch (error) {
                console.error(`Error initializing module ${module.id}:`, error);
            }
        });
    }
    
    /**
     * Get a human-readable module name from its ID
     * @param {string} id - Module ID
     * @returns {string} - User-friendly module name
     */
    function getModuleNameFromId(id) {
        // Convert kebab-case to Title Case
        return id
            .split('-')
            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
            .join(' ');
    }

    /**
     * Create the loading overlay
     */
    function createLoadingOverlay() {
        // Find existing elements or create minimal ones for progress reporting
        loadingOverlay = document.querySelector('.loading-overlay');
        
        if (!loadingOverlay) {
            // If no overlay exists in the HTML, create a minimal one
            loadingOverlay = document.createElement('div');
            loadingOverlay.className = 'loading-overlay';
            loadingOverlay.style.transition = 'opacity 0.5s ease-out';
            document.body.appendChild(loadingOverlay);
            
            const content = document.createElement('div');
            content.className = 'loading-content';
            
            const title = document.createElement('h2');
            title.textContent = 'Loading Interface';
            content.appendChild(title);
            
            const progressBar = document.createElement('div');
            progressBar.className = 'loading-bar';
            
            progressIndicator = document.createElement('div');
            progressIndicator.className = 'loading-progress';
            progressBar.appendChild(progressIndicator);
            
            progressText = document.createElement('div');
            progressText.className = 'loading-text';
            progressText.textContent = '0%';
            progressBar.appendChild(progressText);
            
            statusText = document.createElement('div');
            statusText.className = 'loading-status';
            statusText.textContent = 'Loading modules...';
            
            modulesList = document.createElement('ul');
            modulesList.id = 'modules-list';
            
            content.appendChild(progressBar);
            content.appendChild(statusText);
            content.appendChild(modulesList);
            loadingOverlay.appendChild(content);
        } else {
            // If overlay exists, find the progress elements
            progressIndicator = loadingOverlay.querySelector('.loading-progress');
            progressText = loadingOverlay.querySelector('.loading-text');
            statusText = loadingOverlay.querySelector('.loading-status');
            modulesList = loadingOverlay.querySelector('#modules-list');
            
            // Ensure transition is set
            loadingOverlay.style.transition = 'opacity 0.5s ease-out';
        }
    }

    /**
     * Create a module list item in the UI
     * @param {string} id - Module ID
     * @param {string} name - Module display name
     */
    function createModuleListItem(id, name) {
        if (!modulesList) return;
        
        // Check if we've already created this module item
        if (createdModules.has(id)) return;
        
        // Mark this module as created
        createdModules.add(id);
        
        const moduleItem = document.createElement('li');
        moduleItem.className = 'module-item';
        moduleItem.id = `module-${id}`;
        moduleItem.innerHTML = `
            <span class="module-name">${name}</span>
            <span class="module-status status-pending">Pending</span>
        `;
        modulesList.appendChild(moduleItem);
    }

    /**
     * Handle module progress events
     */
    function handleModuleProgress(event) {
        const { moduleId, progress } = event.detail;
        updateModuleProgress(moduleId, progress);
        updateOverallProgress();
    }

    /**
     * Handle module state change events
     */
    function handleModuleStateChange(event) {
        const { moduleId, state } = event.detail;
        updateModuleState(moduleId, state);
        updateOverallProgress();
        
        // Check if all modules are finished after each state change
        checkAllFinished();
    }

    /**
     * Handle module status message events
     */
    function handleModuleMessage(event) {
        const { moduleId, message } = event.detail;
        updateModuleStatusText(moduleId, message);
    }

    /**
     * Check if all modules are finished loading
     */
    function checkAllFinished() {
        const modules = moduleRegistry.getAllModules();
        const allFinished = Object.values(modules).every(module => {
            const state = module.getState();
            return state === ModuleState.FINISHED || state === ModuleState.ERROR;
        });
        
        if (allFinished && !isLoadingComplete) {
            finalizeLoading();
        }
    }

    /**
     * Finalize the loading process
     */
    function finalizeLoading() {
        console.log('Loading completed. Finalizing...');
        completeFinalization();
    }

    /**
     * Complete the finalization process
     */
    function completeFinalization() {
        isLoadingComplete = true;

        // Call the start method on the game loop module directly
        // Ensure the game loop module was found during initialization
        if (gameLoopModule && typeof gameLoopModule.start === 'function') {
             // Hide the overlay first, then start the game loop
             hideOverlay(() => {
                 console.log("Loader: Overlay hidden, starting Game Loop.");
                 gameLoopModule.start();
             });
        } else {
            console.error("Loader: Game Loop module not found or start method missing.");
            // Hide overlay anyway, but log error
            hideOverlay();
        }
    }

    /**
     * Hide the loading overlay with a fade out animation
     * Then completely remove it from the DOM
     * @param {Function} [callback] - Optional callback to execute after fade completes
     */
    function hideOverlay(callback) { // Added callback parameter
        if (!loadingOverlay) {
            if (callback) callback(); // Call callback immediately if no overlay
            return;
        }

        // Set opacity to 0 to trigger the fade-out transition
        loadingOverlay.style.opacity = '0';

        // Use transition event listener to remove from DOM after fade completes
        loadingOverlay.addEventListener('transitionend', function handler(e) {
            // Only handle the opacity transition
            if (e.propertyName === 'opacity') {
                console.log('Module Loader: Removing overlay from DOM');

                // Remove from DOM completely
                if (loadingOverlay.parentNode) {
                    loadingOverlay.parentNode.removeChild(loadingOverlay);
                }

                // Remove the event listener to prevent memory leaks
                loadingOverlay.removeEventListener('transitionend', handler);

                // Set to null to allow garbage collection
                loadingOverlay = null;

                // Execute the callback if provided
                if (callback) callback();
            }
        });

        // Fallback in case the transition event doesn't fire
        setTimeout(() => {
            if (loadingOverlay && loadingOverlay.parentNode) {
                console.log('Module Loader: Removing overlay from DOM (fallback)');
                loadingOverlay.parentNode.removeChild(loadingOverlay);
                loadingOverlay = null;
            }
            // Execute callback in fallback as well
            if (callback) callback();
        }, 1000); // Wait longer than the transition duration
    }

    /**
     * Update the state of a module
     * @param {string} id - Module ID
     * @param {string} state - New state
     */
    function updateModuleState(id, state) {
        // Update UI
        const moduleItem = document.getElementById(`module-${id}`);
        if (!moduleItem) return;
        
        const statusElement = moduleItem.querySelector('.module-status');
        if (!statusElement) return;
        
        // Remove all status classes
        statusElement.classList.remove(
            'status-pending',
            'status-loading',
            'status-waiting',
            'status-initializing',
            'status-finished',
            'status-error'
        );
        
        // Add appropriate class and text
        let statusText = '';
        switch (state) {
            case ModuleState.PENDING:
                statusElement.classList.add('status-pending');
                statusText = 'Pending';
                break;
            case ModuleState.LOADING:
                statusElement.classList.add('status-loading');
                statusText = 'Loading';
                break;
            case ModuleState.WAITING:
                statusElement.classList.add('status-waiting');
                statusText = 'Waiting';
                break;
            case ModuleState.INITIALIZING:
                statusElement.classList.add('status-initializing');
                statusText = 'Initializing';
                break;
            case ModuleState.FINISHED:
                statusElement.classList.add('status-finished');
                statusText = 'Finished';
                break;
            case ModuleState.ERROR:
                statusElement.classList.add('status-error');
                statusText = 'Error';
                break;
        }
        
        statusElement.textContent = statusText;
    }

    /**
     * Update the progress of a module
     * @param {string} id - Module ID
     * @param {number} progress - Progress percentage (0-100)
     */
    function updateModuleProgress(id, progress) {
        // Module states are now managed by the module itself
        
        // Update any additional UI elements for module progress if needed
        const moduleItem = document.getElementById(`module-${id}`);
        if (moduleItem) {
            // Update progress display if needed
        }
    }
    
    /**
     * Update the status text of a module in the UI
     * @param {string} id - Module ID
     * @param {string} text - Status text to display
     */
    function updateModuleStatusText(id, text) {
        const moduleItem = document.getElementById(`module-${id}`);
        if (!moduleItem) return;
        
        let statusDetailElement = moduleItem.querySelector('.module-status-detail');
        
        if (!statusDetailElement) {
            statusDetailElement = document.createElement('span');
            statusDetailElement.className = 'module-status-detail';
            moduleItem.appendChild(statusDetailElement);
        }
        
        statusDetailElement.textContent = text;
    }

    /**
     * Update overall progress based on module weights and progress
     */
    function updateOverallProgress() {
        const modules = moduleRegistry.getAllModules();
        const moduleIds = Object.keys(modules);
        
        // Calculate total weight
        const totalWeight = moduleIds.reduce((sum, id) => {
            return sum + (moduleWeights[id] || 1);
        }, 0);
        
        // Calculate weighted progress
        let overallProgress = moduleIds.reduce((sum, id) => {
            const module = modules[id];
            const weight = moduleWeights[id] || 1;
            return sum + (module.progress * weight / totalWeight);
        }, 0);
        
        overallProgress = Math.min(Math.round(overallProgress), 100);
        
        // Update progress bar
        if (progressIndicator) {
            progressIndicator.style.width = `${overallProgress}%`;
        }
        
        if (progressText) {
            progressText.textContent = `${overallProgress}%`;
        }
        
        // Update status text based on progress
        if (statusText) {
            if (overallProgress >= 100 && !isLoadingComplete) {
                statusText.textContent = 'Finalizing...';
            } else if (isLoadingComplete) {
                statusText.textContent = 'Complete!';
            }
        }
    }

    // Public API
    return {
        init,
        ModuleState
    };
})();

// Now that ModuleLoader is defined, add the event listener
document.addEventListener('DOMContentLoaded', () => {
    // Start the loading process when the DOM is loaded
    ModuleLoader.init();
});
