import { StandardLibraryManager } from './standard-libraries.js';
import { getCoreRuntimeLibraries, isCoreRuntimeLibrary } from './core-libraries.js';
import { resourceManager } from './resource-manager.js';
import { LibraryDependencyResolver } from './library-dependency-resolver.js';
const LIBRARY_LOADER_COMPONENT_ID = 'mj-react-runtime-library-loader-singleton';
export class LibraryLoader {
    static { this.loadedResources = new Map(); }
    static { this.loadedLibraryStates = new Map(); }
    static { this.dependencyResolver = new LibraryDependencyResolver({ debug: false }); }
    static { this.enableProgressiveDelay = false; }
    static preloadCoreScripts(debug = false) {
        if (typeof document === 'undefined')
            return;
        const coreLibraries = getCoreRuntimeLibraries(debug);
        for (const lib of coreLibraries) {
            if (document.querySelector(`link[href="${lib.cdnUrl}"]`) ||
                document.querySelector(`script[src="${lib.cdnUrl}"]`)) {
                continue;
            }
            const link = document.createElement('link');
            link.rel = 'preload';
            link.as = 'script';
            link.href = lib.cdnUrl;
            document.head.appendChild(link);
        }
    }
    static async loadAllLibraries(config, additionalLibraries, options) {
        if (config) {
            StandardLibraryManager.setConfiguration(config);
        }
        if (additionalLibraries && additionalLibraries.length > 0) {
            const currentConfig = StandardLibraryManager.getConfiguration();
            const mergedConfig = {
                libraries: [...currentConfig.libraries, ...additionalLibraries],
                metadata: {
                    ...currentConfig.metadata,
                    lastUpdated: new Date().toISOString()
                }
            };
            StandardLibraryManager.setConfiguration(mergedConfig);
        }
        return this.loadLibrariesFromConfig(undefined, options?.debug);
    }
    static async loadLibrariesFromConfig(options, debug) {
        const coreLibraries = getCoreRuntimeLibraries(debug);
        const corePromises = coreLibraries.map(lib => this.loadScript(lib.cdnUrl, lib.globalVariable, debug, lib.fallbackCdnUrls));
        const coreResults = await Promise.all(corePromises);
        const React = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'React');
        const ReactDOM = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'ReactDOM');
        const Babel = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'Babel');
        if (typeof window !== 'undefined') {
            if (React && !window.React) {
                window.React = React;
            }
            if (ReactDOM && !window.ReactDOM) {
                window.ReactDOM = ReactDOM;
            }
            if (!window.PropTypes) {
                window.PropTypes = {};
            }
        }
        const config = StandardLibraryManager.getConfiguration();
        const enabledLibraries = StandardLibraryManager.getEnabledLibraries();
        let pluginLibraries = enabledLibraries.filter(lib => !isCoreRuntimeLibrary(lib.id));
        if (options) {
            if (options.categories) {
                pluginLibraries = pluginLibraries.filter(lib => options.categories.includes(lib.category));
            }
            if (options.excludeRuntimeOnly) {
                pluginLibraries = pluginLibraries.filter(lib => !lib.isRuntimeOnly);
            }
        }
        pluginLibraries.forEach(lib => {
            if (lib.cdnCssUrl) {
                this.loadCSS(lib.cdnCssUrl);
            }
        });
        const pluginPromises = pluginLibraries.map(lib => this.loadScript(lib.cdnUrl, lib.globalVariable, debug, lib.fallbackCdnUrls));
        const pluginResults = await Promise.all(pluginPromises);
        const libraries = {};
        pluginLibraries.forEach((lib, index) => {
            libraries[lib.globalVariable] = pluginResults[index];
        });
        return {
            React: React || window.React,
            ReactDOM: ReactDOM || window.ReactDOM,
            Babel: Babel || window.Babel,
            libraries
        };
    }
    static async loadLibraries(options) {
        const { loadCore = true, loadUI = true, loadCSS = true, customLibraries = [] } = options;
        const categoriesToLoad = ['runtime'];
        if (loadCore) {
            categoriesToLoad.push('utility', 'charting');
        }
        if (loadUI) {
            categoriesToLoad.push('ui');
        }
        const result = await this.loadLibrariesFromConfig({
            categories: categoriesToLoad
        });
        if (customLibraries.length > 0) {
            const customPromises = customLibraries.map(({ url, globalName }) => this.loadScript(url, globalName));
            const customResults = await Promise.all(customPromises);
            customLibraries.forEach(({ globalName }, index) => {
                result.libraries[globalName] = customResults[index];
            });
        }
        return result;
    }
    static async loadScript(url, globalName, debug = false, fallbackUrls) {
        const existingGlobal = typeof window !== 'undefined' ? window[globalName] : undefined;
        if (existingGlobal) {
            if (debug) {
                console.log(`✅ Library '${globalName}' already available globally`);
            }
            return existingGlobal;
        }
        const existing = this.loadedResources.get(url);
        if (existing) {
            if (debug) {
                console.log(`✅ Library '${globalName}' already loaded (cached)`);
            }
            return existing.promise;
        }
        try {
            return await this.loadScriptFromUrl(url, globalName, debug);
        }
        catch (primaryError) {
            if (!fallbackUrls || fallbackUrls.length === 0) {
                throw primaryError;
            }
            this.cleanupFailedScript(url);
            if (debug) {
                console.warn(`⚠️ Primary CDN failed for '${globalName}', trying fallback CDNs...`);
            }
            for (let i = 0; i < fallbackUrls.length; i++) {
                const fallbackUrl = fallbackUrls[i];
                try {
                    const result = await this.loadScriptFromUrl(fallbackUrl, globalName, debug);
                    console.log(`✅ Library '${globalName}' loaded from fallback CDN: ${fallbackUrl}`);
                    return result;
                }
                catch (fallbackError) {
                    this.cleanupFailedScript(fallbackUrl);
                    if (debug) {
                        console.warn(`⚠️ Fallback CDN ${i + 1}/${fallbackUrls.length} failed for '${globalName}'`);
                    }
                }
            }
            const allUrls = [url, ...fallbackUrls];
            throw new Error(`Failed to load '${globalName}' from all CDN sources: ${allUrls.join(', ')}`);
        }
    }
    static cleanupFailedScript(url) {
        const failed = this.loadedResources.get(url);
        if (failed?.element?.parentNode) {
            failed.element.parentNode.removeChild(failed.element);
        }
        this.loadedResources.delete(url);
    }
    static loadScriptFromUrl(url, globalName, debug = false) {
        const existing = this.loadedResources.get(url);
        if (existing) {
            return existing.promise;
        }
        const promise = new Promise((resolve, reject) => {
            const existingGlobal = window[globalName];
            if (existingGlobal) {
                resolve(existingGlobal);
                return;
            }
            const existingScript = document.querySelector(`script[src="${url}"]`);
            if (existingScript) {
                this.waitForScriptLoad(existingScript, globalName, resolve, reject);
                return;
            }
            const script = document.createElement('script');
            script.src = url;
            script.async = true;
            const cleanup = () => {
                script.removeEventListener('load', onLoad);
                script.removeEventListener('error', onError);
            };
            const onLoad = async () => {
                cleanup();
                if (LibraryLoader.enableProgressiveDelay) {
                    try {
                        const global = await LibraryLoader.waitForGlobalVariable(globalName, url, debug);
                        resolve(global);
                    }
                    catch (error) {
                        reject(error);
                    }
                }
                else {
                    const global = window[globalName];
                    if (global) {
                        if (debug) {
                            console.log(`✅ Library '${globalName}' loaded successfully from ${url}`);
                        }
                        resolve(global);
                    }
                    else {
                        resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => {
                            const delayedGlobal = window[globalName];
                            if (delayedGlobal) {
                                if (debug) {
                                    console.log(`✅ Library '${globalName}' loaded successfully (delayed initialization)`);
                                }
                                resolve(delayedGlobal);
                            }
                            else {
                                reject(new Error(`${globalName} not found after script load`));
                            }
                        }, 100, { url, globalName });
                    }
                }
            };
            const onError = () => {
                cleanup();
                reject(new Error(`Failed to load script: ${url}`));
            };
            script.addEventListener('load', onLoad);
            script.addEventListener('error', onError);
            if (debug) {
                console.log(`📦 Loading library '${globalName}' from ${url}...`);
            }
            document.head.appendChild(script);
            resourceManager.registerDOMElement(LIBRARY_LOADER_COMPONENT_ID, script);
        });
        this.loadedResources.set(url, {
            element: document.querySelector(`script[src="${url}"]`),
            promise
        });
        return promise;
    }
    static isLibraryReady(globalVariable) {
        if (!globalVariable) {
            return false;
        }
        if (typeof globalVariable === 'function') {
            return true;
        }
        if (typeof globalVariable === 'object') {
            const keys = Object.keys(globalVariable);
            return keys.length > 0 ||
                Object.getOwnPropertyNames(globalVariable).length > 1 ||
                globalVariable.constructor !== Object;
        }
        return true;
    }
    static async waitForGlobalVariable(globalName, url, debug = false) {
        const delays = [0, 100, 200, 300, 400];
        const maxAttempts = delays.length;
        let totalDelay = 0;
        for (let attempt = 0; attempt < maxAttempts; attempt++) {
            if (attempt > 0) {
                const delay = delays[attempt];
                if (debug) {
                    console.log(`⏳ Waiting ${delay}ms for ${globalName} to initialize (attempt ${attempt + 1}/${maxAttempts})...`);
                }
                await new Promise(resolve => {
                    resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => resolve(undefined), delay, { globalName, attempt });
                });
                totalDelay += delay;
            }
            const global = window[globalName];
            if (global) {
                const isReady = this.isLibraryReady(global);
                if (isReady) {
                    if (debug) {
                        if (totalDelay > 0) {
                            console.log(`✅ ${globalName} ready after ${totalDelay}ms delay`);
                        }
                        else {
                            console.log(`✅ Library '${globalName}' loaded successfully from ${url}`);
                        }
                    }
                    return global;
                }
                else if (debug && attempt < maxAttempts - 1) {
                    console.log(`🔄 ${globalName} exists but not fully initialized, will retry...`);
                }
            }
        }
        const finalGlobal = window[globalName];
        if (finalGlobal) {
            console.warn(`⚠️ ${globalName} loaded but may not be fully initialized after ${totalDelay}ms`);
            return finalGlobal;
        }
        throw new Error(`${globalName} not found after script load and ${totalDelay}ms delay`);
    }
    static loadCSS(url) {
        if (this.loadedResources.has(url)) {
            return;
        }
        const existingLink = document.querySelector(`link[href="${url}"]`);
        if (existingLink) {
            return;
        }
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = url;
        document.head.appendChild(link);
        resourceManager.registerDOMElement(LIBRARY_LOADER_COMPONENT_ID, link);
        this.loadedResources.set(url, {
            element: link,
            promise: Promise.resolve()
        });
    }
    static waitForScriptLoad(script, globalName, resolve, reject) {
        const checkGlobal = () => {
            const global = window[globalName];
            if (global) {
                resolve(global);
            }
            else {
                resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => {
                    const delayedGlobal = window[globalName];
                    if (delayedGlobal) {
                        resolve(delayedGlobal);
                    }
                    else {
                        reject(new Error(`${globalName} not found after script load`));
                    }
                }, 100, { context: 'waitForScriptLoad', globalName });
            }
        };
        if (script.complete || script.readyState === 'complete') {
            checkGlobal();
            return;
        }
        const loadHandler = () => {
            checkGlobal();
        };
        resourceManager.addEventListener(LIBRARY_LOADER_COMPONENT_ID, script, 'load', loadHandler, { once: true });
    }
    static getLoadedResources() {
        return this.loadedResources;
    }
    static clearCache() {
        this.loadedResources.forEach((resource, url) => {
            if (resource.element && resource.element.parentNode) {
                resource.element.parentNode.removeChild(resource.element);
            }
        });
        this.loadedResources.clear();
        this.loadedLibraryStates.clear();
        resourceManager.cleanupComponent(LIBRARY_LOADER_COMPONENT_ID);
    }
    static async loadLibraryWithDependencies(libraryName, allLibraries, requestedBy = 'user', options) {
        const debug = options?.debug || false;
        if (debug) {
            console.log(`📚 Loading library '${libraryName}' with dependencies`);
        }
        const existingState = this.loadedLibraryStates.get(libraryName);
        if (existingState) {
            if (debug) {
                console.log(`✅ Library '${libraryName}' already loaded (version: ${existingState.version})`);
            }
            if (!existingState.requestedBy.includes(requestedBy)) {
                existingState.requestedBy.push(requestedBy);
            }
            return window[existingState.globalVariable];
        }
        const loadOrderResult = this.dependencyResolver.getLoadOrder([libraryName], allLibraries, options);
        if (!loadOrderResult.success) {
            const errors = loadOrderResult.errors?.join(', ') || 'Unknown error';
            throw new Error(`Failed to resolve dependencies for '${libraryName}': ${errors}`);
        }
        if (loadOrderResult.warnings && debug) {
            console.warn(`⚠️ Warnings for '${libraryName}':`, loadOrderResult.warnings);
        }
        const loadOrder = loadOrderResult.order || [];
        if (debug) {
            console.log(`📋 Load order for '${libraryName}':`, loadOrder.map(lib => `${lib.Name}@${lib.Version}`));
        }
        for (const library of loadOrder) {
            if (this.loadedLibraryStates.has(library.Name)) {
                if (debug) {
                    console.log(`⏭️ Skipping '${library.Name}' (already loaded)`);
                }
                continue;
            }
            if (library.Status) {
                if (library.Status === 'Disabled') {
                    console.error(`🚫 ERROR: Library '${library.Name}' is DISABLED and should not be used`);
                }
                else if (library.Status === 'Deprecated') {
                    console.warn(`⚠️ WARNING: Library '${library.Name}' is DEPRECATED. Consider using an alternative.`);
                }
            }
            if (debug) {
                console.log(`📥 Loading '${library.Name}@${library.Version}'`);
            }
            if (!library.CDNUrl || !library.GlobalVariable) {
                throw new Error(`Library '${library.Name}' missing CDN URL or global variable`);
            }
            if (library.CDNCssUrl) {
                const cssUrls = library.CDNCssUrl.split(',').map(url => url.trim());
                for (const cssUrl of cssUrls) {
                    if (cssUrl) {
                        this.loadCSS(cssUrl);
                    }
                }
            }
            const loadedGlobal = await this.loadScript(library.CDNUrl, library.GlobalVariable, debug);
            const dependencies = Array.from(this.dependencyResolver.getDirectDependencies(library).keys());
            this.loadedLibraryStates.set(library.Name, {
                name: library.Name,
                version: library.Version || 'unknown',
                globalVariable: library.GlobalVariable,
                loadedAt: new Date(),
                requestedBy: library.Name === libraryName ? [requestedBy] : [],
                dependencies
            });
            if (debug) {
                console.log(`✅ Loaded '${library.Name}@${library.Version}'`);
            }
        }
        const targetLibrary = loadOrder.find(lib => lib.Name === libraryName);
        if (!targetLibrary || !targetLibrary.GlobalVariable) {
            throw new Error(`Failed to load library '${libraryName}'`);
        }
        return window[targetLibrary.GlobalVariable];
    }
    static async loadLibrariesWithDependencies(libraryNames, allLibraries, requestedBy = 'user', options) {
        const debug = options?.debug || false;
        const result = new Map();
        if (debug) {
            console.log(`📚 Loading libraries with dependencies:`, libraryNames);
            console.log(`  📦 Total available libraries: ${allLibraries.length}`);
        }
        const loadOrderResult = this.dependencyResolver.getLoadOrder(libraryNames, allLibraries, options);
        if (!loadOrderResult.success) {
            const errors = loadOrderResult.errors?.join(', ') || 'Unknown error';
            throw new Error(`Failed to resolve dependencies: ${errors}`);
        }
        if (debug) {
            console.log(`  📊 Dependency resolution result:`, {
                success: loadOrderResult.success,
                errors: loadOrderResult.errors || [],
                warnings: loadOrderResult.warnings || []
            });
            if (loadOrderResult.order) {
                console.log(`  🔄 Resolved dependencies for each library:`);
                loadOrderResult.order.forEach(lib => {
                    const deps = this.dependencyResolver.parseDependencies(lib.Dependencies);
                    if (deps.size > 0) {
                        console.log(`    • ${lib.Name}@${lib.Version} requires:`, Array.from(deps.entries()));
                    }
                    else {
                        console.log(`    • ${lib.Name}@${lib.Version} (no dependencies)`);
                    }
                });
            }
        }
        if (loadOrderResult.warnings && debug) {
            console.warn(`  ⚠️ Warnings:`, loadOrderResult.warnings);
        }
        const loadOrder = loadOrderResult.order || [];
        if (debug) {
            console.log(`  📋 Final load order:`, loadOrder.map(lib => `${lib.Name}@${lib.Version}`));
        }
        for (const library of loadOrder) {
            if (this.loadedLibraryStates.has(library.Name)) {
                if (debug) {
                    console.log(`⏭️ Skipping '${library.Name}' (already loaded)`);
                }
                const state = this.loadedLibraryStates.get(library.Name);
                if (libraryNames.includes(library.Name)) {
                    result.set(library.Name, window[state.globalVariable]);
                }
                continue;
            }
            if (library.Status) {
                if (library.Status === 'Disabled') {
                    console.error(`🚫 ERROR: Library '${library.Name}' is DISABLED and should not be used`);
                }
                else if (library.Status === 'Deprecated') {
                    console.warn(`⚠️ WARNING: Library '${library.Name}' is DEPRECATED. Consider using an alternative.`);
                }
            }
            if (debug) {
                console.log(`📥 Loading '${library.Name}@${library.Version}'`);
            }
            if (!library.CDNUrl || !library.GlobalVariable) {
                throw new Error(`Library '${library.Name}' missing CDN URL or global variable`);
            }
            if (library.CDNCssUrl) {
                const cssUrls = library.CDNCssUrl.split(',').map(url => url.trim());
                for (const cssUrl of cssUrls) {
                    if (cssUrl) {
                        this.loadCSS(cssUrl);
                    }
                }
            }
            const loadedGlobal = await this.loadScript(library.CDNUrl, library.GlobalVariable, debug);
            const dependencies = Array.from(this.dependencyResolver.getDirectDependencies(library).keys());
            this.loadedLibraryStates.set(library.Name, {
                name: library.Name,
                version: library.Version || 'unknown',
                globalVariable: library.GlobalVariable,
                loadedAt: new Date(),
                requestedBy: libraryNames.includes(library.Name) ? [requestedBy] : [],
                dependencies
            });
            if (libraryNames.includes(library.Name)) {
                result.set(library.Name, loadedGlobal);
            }
            if (debug) {
                console.log(`✅ Loaded '${library.Name}@${library.Version}'`);
            }
        }
        return result;
    }
    static getLoadedLibraryStates() {
        return new Map(this.loadedLibraryStates);
    }
    static isLibraryLoaded(libraryName) {
        return this.loadedLibraryStates.has(libraryName);
    }
    static getLoadedLibraryVersion(libraryName) {
        return this.loadedLibraryStates.get(libraryName)?.version;
    }
}
//# sourceMappingURL=library-loader.js.map