/**
 * 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';

// Ensure moduleRegistry is available globally before anything else runs
window.moduleRegistry = moduleRegistry;
console.log('Module registry initialized and assigned to window.moduleRegistry');

/**
 * Module States
 */
const ModuleState = {
    PENDING: 'PENDING',
    LOADING: 'LOADING',
    FETCHING: 'FETCHING',  // Added new state for fetching resources
    WAITING: 'WAITING',
    INITIALIZING: 'INITIALIZING',
    FINISHED: 'FINISHED',
    ERROR: 'ERROR'
};

const MODULE_CACHE_BUSTER = '20260516-scroll-window';
window.MODULE_CACHE_BUSTER = MODULE_CACHE_BUSTER;

/**
 * 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
    let moduleTimings = {}; // Track timing data for modules

    /**
     * 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().catch(error => {
                console.error('Module Loader: Initialization failed:', error);
                finalizeLoading();
            });
        });
    }

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

    /**
     * Load all module scripts
     * @returns {Promise} - Resolves when all module scripts are loaded
     */
    async function loadModuleScripts() {
        
        // Define dependency scripts that need to be loaded first but aren't modules themselves
        const dependenciesToLoad = [
            { script: '/js/api-tts-module-base.js' }, // Abstract base class, not a module
            { script: '/js/tts-handler-module.js' }  // Abstract base class for TTS handlers, not a module
        ];
        
        // Define modules with their weights
        let modulesToLoad = [
            // Core functionality modules
            { id: 'persistence-manager', script: '/js/persistence-manager-module.js', weight: 12 },
            { id: 'localization', script: '/js/localization-module.js', weight: 12 },
            { id: 'story-history', script: '/js/story-history-module.js', weight: 8 },
            { id: 'game-config', script: '/js/game-config-module.js', weight: 8 },
            { id: 'text-processor', script: '/js/text-processor-module.js', weight: 15 },
            { id: 'markup-parser', script: '/js/markup-parser-module.js', weight: 5 },
            { id: 'paragraph-layout', script: '/js/paragraph-layout-module.js', weight: 17 },
            { id: 'sentence-queue', script: '/js/sentence-queue-module.js', weight: 12 },
            { id: 'layout-renderer', script: '/js/layout-renderer-module.js', weight: 13 }, // Add Layout Renderer module
            { id: 'animation-queue', script: '/js/animation-queue-module.js', weight: 12 },
            { id: 'playback-coordinator', script: '/js/playback-coordinator-module.js', weight: 8 }, // Synchronizes animation + TTS

            // Audio and TTS modules
            { id: 'audio-manager', script: '/js/audio-manager-module.js', weight: 12 },
            { id: 'kokoro-tts', script: '/js/kokoro-tts-module.js', weight: 50 },
            { id: 'browser-tts', script: '/js/browser-tts-module.js', weight: 12 },
            { id: 'elevenlabs-tts', script: '/js/elevenlabs-tts-module.js', weight: 12 },
            { id: 'openai-tts', script: '/js/openai-tts-module.js', weight: 12 },
            { id: 'tts-factory', script: '/js/tts-factory-module.js', weight: 13 }, // TTSFactory must be loaded before TTSPlayer
            
            // UI and interaction modules
            { id: 'text-buffer', script: '/js/text-buffer-module.js', weight: 12 },
            { id: 'ui-effects', script: '/js/ui-effects-module.js', weight: 12 },        // Add UI Effects module
            { id: 'ui-input-handler', script: '/js/ui-input-handler-module.js', weight: 27 }, // Add UI Input Handler module
            { id: 'ui-display-handler', script: '/js/ui-display-handler-module.js', weight: 27 }, // Add UI Display Handler module
            { id: 'choice-display', script: '/js/choice-display-module.js', weight: 8 },
            { id: 'ui-controller', script: '/js/ui-controller-module.js', weight: 27 },
            { id: 'options-ui', script: '/js/options-ui-module.js', weight: 13 },
            { id: 'socket-client', script: '/js/socket-client-module.js', weight: 17 },
            
            // Main game module - should be last to load
            { id: 'game-loop', script: '/js/game-loop-module.js', weight: 27 }
        ];
        
        // 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 => {
            createModuleItem(module.id, getModuleNameFromId(module.id));
        });
        
        // Load dependencies first
        const loadDependencies = dependenciesToLoad.map(dependency => loadScript(dependency.script));
        await Promise.all(loadDependencies);
        
        // Load each module script
        const loadPromises = modulesToLoad.map(module => loadScript(module.script));
        const loadResult = await Promise.all(loadPromises);
        
        // Wait briefly for modules to register
        await new Promise(resolve => setTimeout(resolve, 100));
        
        // Analyze dependencies and detect circular references
        analyzeModuleDependencies();
        
        return loadResult;
    }
    
    /**
     * Analyze module dependencies to detect circular references and print detailed diagnostics
     */
    function analyzeModuleDependencies() {
        const registry = window.moduleRegistry;
        if (!registry || !registry.modules) {
            console.error("Module Registry not available for dependency analysis");
            return;
        }
        
        // Build dependency graph
        const graph = {};
        
        // Initialize the graph with all modules
        Object.keys(registry.modules).forEach(moduleId => {
            graph[moduleId] = [];
        });
        
        // Add dependencies to graph
        Object.entries(registry.modules).forEach(([moduleId, module]) => {
            if (module.dependencies && Array.isArray(module.dependencies)) {
                module.dependencies.forEach(depId => {
                    // Check if dependency exists
                    if (!registry.modules[depId]) {
                        console.warn(`Module ${moduleId} depends on missing module ${depId}`);
                    } else {
                        graph[moduleId].push(depId);
                    }
                });
            }
        });
        
        // Detect circular dependencies using DFS
        const detectCycles = () => {
            const visited = {};
            const recStack = {};
            const cycles = [];
            const pathStack = [];
            
            const dfs = (node, path = []) => {
                // Node is already in recursion stack - we found a cycle
                if (recStack[node]) {
                    const cycleStart = path.indexOf(node);
                    if (cycleStart !== -1) {
                        const cycle = path.slice(cycleStart).concat(node);
                        cycles.push(cycle);
                        return true;
                    }
                }
                
                // If already visited and not in recursion, no cycle through this node
                if (visited[node]) {
                    return false;
                }
                
                // Mark node as visited and add to recursion stack
                visited[node] = true;
                recStack[node] = true;
                pathStack.push(node);
                
                // Visit all neighbors
                const hasCycle = graph[node].some(neighbor => {
                    return dfs(neighbor, [...pathStack]);
                });
                
                // Remove from recursion stack and path
                recStack[node] = false;
                pathStack.pop();
                
                return hasCycle;
            };
            
            // Start DFS from each node
            Object.keys(graph).forEach(node => {
                if (!visited[node]) {
                    dfs(node);
                }
            });
            
            return cycles;
        };
        
        // Find all circular dependencies
        const cycles = detectCycles();
        
        // Display detailed information about circular dependencies
        if (cycles.length > 0) {
            console.group("%cCircular Dependencies Detected", "color: red; font-weight: bold");
            cycles.forEach((cycle, index) => {
                console.log(`%cCircular Dependency Chain ${index + 1}:`, "font-weight: bold");
                
                // Print the cycle with dependency details
                cycle.forEach((moduleId, i) => {
                    const module = registry.modules[moduleId] || { name: 'Unknown' };
                    const nextModuleId = cycle[(i + 1) % cycle.length];
                    const nextModule = registry.modules[nextModuleId] || { name: 'Unknown' };
                    console.log(
                        `%c${moduleId}%c (${module.name}) depends on %c${nextModuleId}%c (${nextModule.name})`,
                        "color: blue; font-weight: bold",
                        "color: black",
                        "color: blue; font-weight: bold",
                        "color: black"
                    );
                    
                    // Print the actual dependencies declared by this module
                    if (module.dependencies && Array.isArray(module.dependencies)) {
                        console.log(`  Dependencies declared by ${moduleId}:`, module.dependencies);
                    }
                });
                
                // Suggest potential solutions
                console.log('%cPossible solutions:', 'font-weight: bold');
                console.log('1. Remove one of the dependencies from the chain');
                console.log('2. Use dynamic dependency resolution instead of static dependencies');
                console.log('3. Create an interface module that both modules depend on');
                console.log('4. Refactor module responsibilities to eliminate circular needs');
            });
            console.groupEnd();
        } else {
            console.log("%cNo circular dependencies detected", "color: green; font-weight: bold");
        }
        
        // Calculate an optimized loading order using topological sort
        const calculateOptimalLoadOrder = () => {
            const result = [];
            const visited = {};
            const temp = {}; // Temporary marks for detecting cycles
            
            const visit = (node) => {
                // Node is already in result, skip
                if (visited[node]) return;
                
                // If temp is true, we have a cycle
                if (temp[node]) {
                    console.warn(`Skipping cycle involving ${node} during topological sort`);
                    return;
                }
                
                // Mark node as being processed
                temp[node] = true;
                
                // Process all dependencies first
                if (graph[node]) {
                    graph[node].forEach(dep => visit(dep));
                }
                
                // Mark as visited and add to result
                temp[node] = false;
                visited[node] = true;
                result.push(node);
            };
            
            // Visit all nodes
            Object.keys(graph).forEach(node => {
                if (!visited[node]) {
                    visit(node);
                }
            });
            
            return result; // Dependencies are pushed before dependents.
        };
        
        const optimalOrder = calculateOptimalLoadOrder();
        
        // Print the optimal loading order
        console.group("%cOptimal Module Loading Order", "color: green; font-weight: bold");
        optimalOrder.forEach((moduleId, index) => {
            const module = registry.modules[moduleId] || { name: 'Unknown' };
            console.log(`${index + 1}. %c${moduleId}%c (${module.name})`,
                        "font-weight: bold", "font-weight: normal");
        });
        console.groupEnd();
        
        // Compare with actual loading order and suggest improvements
        console.log("%cRecommended changes to module loading order in loader.js:", "font-weight: bold");
        if (optimalOrder.length > 0) {
            console.log("const modulesToLoad = [");
            optimalOrder.forEach((moduleId, index) => {
                // Generate a reasonable path based on the moduleId
                const scriptPath = `/js/${moduleId}.js`;
                console.log(`    { id: '${moduleId}', script: '${scriptPath}', weight: ${index * 5 + 5} },`);
            });
            console.log("];");
        }
    }
    
    /**
     * Sort modules by their dependencies to create an optimal loading order
     * This function can be used before initialization to ensure modules are loaded in the correct order
     * @param {Array} modules - Array of module objects with id and dependencies
     * @returns {Array} - Sorted array of modules
     */
    function sortModulesByDependencies(modules) {
        // Build a dependency graph
        const graph = {};
        
        // Initialize the graph with all modules
        modules.forEach(module => {
            graph[module.id] = { module, dependencies: [] };
        });
        
        // Add dependencies to the graph
        // We need to do this in a second pass because some modules might reference others that come later in the array
        modules.forEach(module => {
            if (module.dependencies && Array.isArray(module.dependencies)) {
                module.dependencies.forEach(depId => {
                    if (graph[depId]) {
                        graph[module.id].dependencies.push(depId);
                    } else {
                        console.warn(`Module ${module.id} depends on unknown module ${depId}`);
                    }
                });
            }
        });
        
        // Perform a topological sort
        const result = [];
        const visited = {};
        const temp = {}; // For cycle detection
        
        function visit(nodeId) {
            // Node is already in result, skip
            if (visited[nodeId]) return;
            
            // If temp is true, we have a cycle
            if (temp[nodeId]) {
                console.warn(`Circular dependency detected involving ${nodeId}. Skipping.`);
                return;
            }
            
            // Mark node as being processed
            temp[nodeId] = true;
            
            // Process all dependencies first
            if (graph[nodeId] && graph[nodeId].dependencies) {
                graph[nodeId].dependencies.forEach(depId => {
                    visit(depId);
                });
            }
            
            // Mark as visited and add to result
            temp[nodeId] = false;
            visited[nodeId] = true;
            result.push(graph[nodeId].module);
        }
        
        // Visit all nodes
        Object.keys(graph).forEach(nodeId => {
            if (!visited[nodeId]) {
                visit(nodeId);
            }
        });
        
        return result; // Dependencies are pushed before dependents.
    }
    
    /**
     * Load a script dynamically
     * @param {string} src - Script source URL
     * @returns {Promise} - Resolves when script is loaded
     */
    function loadScript(src) {
        // Extract module ID from src path
        const moduleId = src.split('/').pop().replace('.js', '');
        
        // Update state to LOADING if this is a module
        if (moduleId && moduleWeights[moduleId]) {
            // Ensure module item exists in UI
            const moduleItem = document.getElementById(`module-${moduleId}`);
            if (!moduleItem) {
                createModuleItem(moduleId, getModuleNameFromId(moduleId));
            }
            
            // Set initial progress to 0%
            handleModuleProgress({
                detail: { moduleId, progress: 0 }
            });
            
            // Set state to loading
            updateModuleState(moduleId, ModuleState.LOADING);
            
            // Record start time for this module (for timing data)
            if (!moduleTimings[moduleId]) {
                moduleTimings[moduleId] = {};
            }
            moduleTimings[moduleId].startTime = performance.now();
        }
        
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.type = 'module';
            const separator = src.includes('?') ? '&' : '?';
            script.src = `${src}${separator}v=${MODULE_CACHE_BUSTER}`;
            
            // Monitor loading progress using a fake progress indicator (0-10%)
            if (moduleId && moduleWeights[moduleId]) {
                let loadProgress = 0;
                const progressInterval = setInterval(() => {
                    loadProgress = Math.min(loadProgress + 1, 9); // Max 9% until actual load completes
                    handleModuleProgress({
                        detail: { moduleId, progress: loadProgress }
                    });
                }, 100);
                
                script.onload = () => {
                    clearInterval(progressInterval);
                    // Final progress at 10% when script is loaded
                    handleModuleProgress({
                        detail: { moduleId, progress: 10 }
                    });
                    
                    // Record script load complete time
                    if (moduleTimings[moduleId]) {
                        moduleTimings[moduleId].scriptLoadTime = performance.now();
                    }
                    
                    resolve();
                };
                
                script.onerror = (error) => {
                    clearInterval(progressInterval);
                    updateModuleState(moduleId, ModuleState.ERROR);
                    
                    // Record error time
                    if (moduleTimings[moduleId]) {
                        moduleTimings[moduleId].errorTime = performance.now();
                    }
                    
                    reject(new Error(`Failed to load script: ${src}`));
                };
            } else {
                script.onload = () => resolve();
                script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
            }
            
            document.head.appendChild(script);
        });
    }
    
    /**
     * Initialize all registered modules
     */
    async function initializeModules() {
        const modules = moduleRegistry.getAllModules();

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

        // Log dependency information for debugging
        console.log('Module dependencies:');
        Object.keys(modules).forEach(moduleId => {
            const dependencies = moduleRegistry.getDependencies(moduleId);
            console.log(`${moduleId} depends on: ${dependencies.length ? dependencies.join(', ') : 'none'}`); 
        });

        const moduleList = Object.values(modules);
        const initializationOrder = sortModulesByDependencies(moduleList);

        console.group('%cActual Module Initialization Order', 'color: green; font-weight: bold');
        initializationOrder.forEach((module, index) => {
            console.log(`${index + 1}. ${module.id} (${module.name})`);
        });
        console.groupEnd();

        // Initialize in dependency order. This makes the loader guarantee that
        // by the time the overlay disappears, every module has run its own
        // initialize() after its declared dependencies are available.
        for (const module of initializationOrder) {
            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 
                            }
                        });
                    }
                };
                
                // Log start of initialization
                console.log(`Starting initialization of module: ${module.id}`);
                
                // Initialize the module with progress callback
                await module.initializeInterface(progressCallback);
                
                // Log completion of initialization
                console.log(`Completed initialization of module: ${module.id}`);
                
            } catch (error) {
                console.error(`Error initializing module ${module.id}:`, error);
            }

            if (module.id === 'game-loop') {
                gameLoopModule = module;
            }
        }

        checkAllFinished();
    }
    
    /**
     * 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
     * @param {string} id - Module ID
     * @param {string} name - Module name
     * @returns {HTMLLIElement} List item element
     */
    function createModuleItem(id, name) {
        if (!modulesList || createdModules.has(id)) return null;
        createdModules.add(id);
        
        // Create elements dynamically
        const li = document.createElement('li');
        li.id = `module-${id}`;
        li.className = 'module-item';
        
        // Set initial progress to 0 using CSS variable
        li.style.setProperty('--progress-width', '0%');
        
        // Create module name element
        const moduleName = document.createElement('div');
        moduleName.className = 'module-name';
        moduleName.textContent = name;
        
        // Create module status element
        const moduleStatus = document.createElement('div');
        moduleStatus.className = 'module-status status-pending';
        moduleStatus.textContent = 'Pending';
        
        // Create module status details element
        const moduleDetails = document.createElement('div');
        moduleDetails.className = 'module-status-detail';
        moduleDetails.textContent = '';
        
        // Append all elements to the list item
        li.appendChild(moduleName);
        li.appendChild(moduleStatus);
        li.appendChild(moduleDetails);
        
        // Force a reflow to ensure animation works
        void li.offsetWidth;
        
        // Add to modules list
        modulesList.appendChild(li);
        
        return li;
    }

    /**
     * Handle module progress events
     */
    function handleModuleProgress(event) {
        const { moduleId, progress } = event.detail;
        
        // Get the module element
        const moduleItem = document.querySelector(`#module-${moduleId}`);
        if (moduleItem) {
            // Update module item's before pseudo-element width using CSS variable
            moduleItem.style.setProperty('--progress-width', `${progress}%`);
            
            // Also set a data attribute for browsers that don't support CSS variables
            moduleItem.setAttribute('data-progress', progress);
        }
        
        updateOverallProgress();
    }

    /**
     * Handle module state change events
     */
    function handleModuleStateChange(event) {
        const { moduleId, state } = event.detail;
        // Update UI with the new state
        updateModuleState(moduleId, state);
        
        // If module is finished, update overall completion
        if (state === ModuleState.FINISHED) {
            // This triggers only when ALL modules are complete, so modules would be removed too quickly
            // if (areAllModulesComplete()) {
            //    hideLoadingOverlay();
            // }
            const moduleItem = document.getElementById(`module-${moduleId}`);
            if (moduleItem) {
                // Ensure module-finished class is added with a small delay to avoid race conditions
                setTimeout(() => {
                    moduleItem.classList.add('module-finished');
                }, 100);
            }
        }
        
        updateOverallProgress();
        
        // Record timing data
        if (moduleTimings[moduleId]) {
            moduleTimings[moduleId][state] = performance.now();
            
            // If the module is finished or has an error, calculate total time
            if (state === ModuleState.FINISHED || state === ModuleState.ERROR) {
                const startTime = moduleTimings[moduleId].startTime || 0;
                moduleTimings[moduleId].totalTime = performance.now() - startTime;
            }
        }
        
        // 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();
        
        // Add detailed logging of all module states
        console.log('Module states:', Object.entries(modules).map(([id, module]) => {
            return `${id}: ${module.getState()}`;
        }));
        
        // First determine which modules are pending
        const pendingModules = Object.values(modules).filter(module => {
            const state = module.getState();
            return state !== ModuleState.FINISHED && state !== ModuleState.ERROR;
        });
        
        // Log pending modules (if any)
        if (pendingModules.length > 0) {
            console.log('Modules still pending:', pendingModules.map(m => `${m.id} (${m.getState()})`));
        } else {
            console.log('No modules pending - all modules are in FINISHED or ERROR state');
        }
        
        // Determine if all modules are finished based on pendingModules
        const allFinished = pendingModules.length === 0;
        
        if (allFinished && !isLoadingComplete) {
            console.log('All modules finished loading. Proceeding to finalization...');
            finalizeLoading();
        } else if (allFinished && isLoadingComplete) {
            console.log('All modules are finished but isLoadingComplete is already true');
        }
    }
    
    /**
     * Finalize the loading process
     */
    function finalizeLoading() {
        console.log('Loading completed. Finalizing...');
        try {
            // Display timing data
            displayModuleTimings();
            
            completeFinalization();
        } catch (error) {
            console.error('Error during finalization:', error);
            // Force hide the overlay even if there was an error
            hideOverlay();
        }
    }

    /**
     * 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.");
                 try {
                     gameLoopModule.start();
                 } catch (error) {
                     console.error("Error starting Game Loop:", error);
                 }
             });
        } else {
            console.error("Loader: Game Loop module not found or start method missing.");
            // Hide overlay anyway, but log error
            hideOverlay();
        }
    }

    /**
     * Display module timing data to help with weight optimization
     */
    function displayModuleTimings() {
        console.group('Module Loading Performance Data');
        console.log('This data can be used to optimize module weights:');
        
        // Format timing data as tuples [moduleId, totalTime, weight]
        const timingData = Object.entries(moduleTimings)
            .filter(([moduleId, timing]) => timing.totalTime !== undefined)
            .map(([moduleId, timing]) => {
                return [moduleId, Math.round(timing.totalTime), moduleWeights[moduleId] || 1];
            })
            .sort((a, b) => b[1] - a[1]); // Sort by total time (descending)
        
        // Create a table for easy reading
        console.table(timingData.map(([moduleId, time, weight]) => {
            return {
                moduleId,
                'totalTime (ms)': time,
                'current weight': weight,
                'suggested weight': Math.max(1, Math.round(time / 50)) // Simple heuristic based on time
            };
        }));
        
        console.log('Raw timing data:');
        console.table(moduleTimings);
        console.groupEnd();
    }

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

    }

    /**
     * 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',
            'status-fetching'
        );
        
        // Set the new status
        let statusText = 'Unknown';
        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.FETCHING:
                statusElement.classList.add('status-fetching');
                statusText = 'Fetching';
                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 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();
});
