import { Metadata, LogStatus, GetProductionStatus } from '@memberjunction/core';
export class ComponentHierarchyRegistrar {
    constructor(compiler, registry, runtimeContext) {
        this.compiler = compiler;
        this.registry = registry;
        this.runtimeContext = runtimeContext;
    }
    async fetchExternalComponent(spec, contextUser) {
        try {
            const provider = Metadata?.Provider;
            if (!provider || !provider.ExecuteGQL) {
                console.warn('⚠️ [ComponentHierarchyRegistrar] No GraphQL provider available for external registry fetch');
                return null;
            }
            const { GraphQLComponentRegistryClient } = await import('@memberjunction/graphql-dataprovider');
            const graphQLClient = new GraphQLComponentRegistryClient(provider);
            const fullSpec = await graphQLClient.GetRegistryComponent({
                registryName: spec.registry,
                namespace: spec.namespace || 'Global',
                name: spec.name,
                version: spec.version || 'latest'
            });
            if (fullSpec && fullSpec.code) {
                if (!GetProductionStatus()) {
                    LogStatus(`✅ [ComponentHierarchyRegistrar] Fetched external component ${spec.name} with code (${fullSpec.code.length} chars)`);
                }
                return fullSpec;
            }
            else {
                console.warn(`⚠️ [ComponentHierarchyRegistrar] Failed to fetch external component ${spec.name} or no code`);
                return null;
            }
        }
        catch (error) {
            console.error(`❌ [ComponentHierarchyRegistrar] Error fetching external component ${spec.name}:`, error);
            return null;
        }
    }
    async registerHierarchy(rootSpec, options) {
        let resolvedRootSpec = rootSpec;
        if (rootSpec.location === 'registry' && rootSpec.registry && !rootSpec.code) {
            if (!GetProductionStatus()) {
                LogStatus(`🌐 [ComponentHierarchyRegistrar] Fetching external registry component: ${rootSpec.registry}/${rootSpec.name}`);
            }
            resolvedRootSpec = await this.fetchExternalComponent(rootSpec, options.contextUser) || rootSpec;
        }
        const { styles, namespace = 'Global', version = 'v1', continueOnError = true, allowOverride = true } = options;
        const registeredComponents = [];
        const errors = [];
        const warnings = [];
        if (!GetProductionStatus()) {
            LogStatus('🌳 ComponentHierarchyRegistrar.registerHierarchy:', undefined, {
                rootComponent: resolvedRootSpec.name,
                hasLibraries: !!(resolvedRootSpec.libraries && resolvedRootSpec.libraries.length > 0),
                libraryCount: resolvedRootSpec.libraries?.length || 0
            });
        }
        const compiledMap = new Map();
        const specMap = new Map();
        const allLoadedLibraries = new Map();
        const compileOnly = async (spec) => {
            if (!spec.code)
                return { success: true };
            try {
                const validLibraries = spec.libraries?.filter(lib => {
                    if (!lib || typeof lib !== 'object')
                        return false;
                    if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined')
                        return false;
                    if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null')
                        return false;
                    return true;
                });
                const compileOptions = {
                    componentName: spec.name,
                    componentCode: spec.code,
                    styles,
                    libraries: validLibraries,
                    dependencies: spec.dependencies,
                    allLibraries: options.allLibraries
                };
                const result = await this.compiler.compile(compileOptions);
                if (result.success && result.component) {
                    compiledMap.set(spec.name, result.component);
                    specMap.set(spec.name, spec);
                    if (result.loadedLibraries) {
                        result.loadedLibraries.forEach((value, key) => {
                            if (!allLoadedLibraries.has(key)) {
                                allLoadedLibraries.set(key, value);
                                if (!GetProductionStatus()) {
                                    LogStatus(`📚 [registerHierarchy] Added library ${key} to accumulated libraries`);
                                }
                            }
                        });
                    }
                    return { success: true };
                }
                else {
                    return {
                        success: false,
                        error: {
                            componentName: spec.name,
                            error: result.error?.message || 'Unknown compilation error',
                            phase: 'compilation'
                        }
                    };
                }
            }
            catch (error) {
                return {
                    success: false,
                    error: {
                        componentName: spec.name,
                        error: error instanceof Error ? error.message : String(error),
                        phase: 'compilation'
                    }
                };
            }
        };
        const compileQueue = [resolvedRootSpec];
        const visited = new Set();
        while (compileQueue.length > 0) {
            let spec = compileQueue.shift();
            if (visited.has(spec.name))
                continue;
            visited.add(spec.name);
            if (spec.location === 'registry' && spec.registry && !spec.code) {
                const fetched = await this.fetchExternalComponent(spec, options.contextUser);
                if (fetched) {
                    spec = fetched;
                }
                else {
                    console.warn(`⚠️ [ComponentHierarchyRegistrar] Could not fetch external component ${spec.name}, skipping`);
                    continue;
                }
            }
            const result = await compileOnly(spec);
            if (!result.success) {
                errors.push(result.error);
                if (!continueOnError) {
                    return { success: false, registeredComponents, errors, warnings, resolvedSpec: resolvedRootSpec };
                }
            }
            if (spec.dependencies) {
                compileQueue.push(...spec.dependencies);
            }
        }
        if (allLoadedLibraries.size > 0) {
            if (!this.runtimeContext.libraries) {
                this.runtimeContext.libraries = {};
            }
            allLoadedLibraries.forEach((value, key) => {
                this.runtimeContext.libraries[key] = value;
                if (!GetProductionStatus()) {
                    LogStatus(`✅ [registerHierarchy] Added ${key} to runtime context libraries`);
                }
            });
        }
        for (const [name, compiled] of compiledMap) {
            const spec = specMap.get(name);
            const components = {};
            for (const [depName, depCompiled] of compiledMap) {
                const depObject = depCompiled.factory(this.runtimeContext, styles);
                components[depName] = depObject.component;
            }
            const componentObject = compiled.factory(this.runtimeContext, styles, components);
            this.registry.register(spec.name, componentObject, spec.namespace || namespace, version);
            registeredComponents.push(spec.name);
        }
        return {
            success: errors.length === 0,
            registeredComponents,
            errors,
            warnings,
            resolvedSpec: resolvedRootSpec
        };
    }
    async registerSingleComponent(spec, options) {
        const { styles, namespace = 'Global', version = 'v1', allowOverride = true } = options;
        try {
            if (!spec.code) {
                return {
                    success: true,
                    error: undefined
                };
            }
            const existingComponent = this.registry.get(spec.name, namespace, version);
            if (existingComponent && !allowOverride) {
                return {
                    success: false,
                    error: {
                        componentName: spec.name,
                        error: `Component already registered in ${namespace}/${version}`,
                        phase: 'registration'
                    }
                };
            }
            const validLibraries = spec.libraries?.filter(lib => {
                if (!lib || typeof lib !== 'object')
                    return false;
                if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined')
                    return false;
                if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null')
                    return false;
                return true;
            });
            if (!GetProductionStatus()) {
                LogStatus(`🔧 Compiling component ${spec.name} with libraries:`, undefined, {
                    originalCount: spec.libraries?.length || 0,
                    filteredCount: validLibraries?.length || 0,
                    libraries: validLibraries?.map(l => l.name) || []
                });
            }
            const compileOptions = {
                componentName: spec.name,
                componentCode: spec.code,
                styles,
                libraries: validLibraries,
                dependencies: spec.dependencies,
                allLibraries: options.allLibraries
            };
            const compilationResult = await this.compiler.compile(compileOptions);
            if (!compilationResult.success) {
                return {
                    success: false,
                    error: {
                        componentName: spec.name,
                        error: compilationResult.error?.message || 'Unknown compilation error',
                        phase: 'compilation'
                    }
                };
            }
            if (compilationResult.loadedLibraries && compilationResult.loadedLibraries.size > 0) {
                if (!this.runtimeContext.libraries) {
                    this.runtimeContext.libraries = {};
                }
                compilationResult.loadedLibraries.forEach((value, key) => {
                    this.runtimeContext.libraries[key] = value;
                    if (!GetProductionStatus()) {
                        LogStatus(`✅ [registerSingleComponent] Added ${key} to runtime context libraries`);
                    }
                });
            }
            if (!GetProductionStatus()) {
                LogStatus(`🏭 Calling factory for ${spec.name} with runtime context:`, undefined, {
                    hasReact: !!this.runtimeContext.React,
                    hasReactDOM: !!this.runtimeContext.ReactDOM,
                    libraryCount: Object.keys(this.runtimeContext.libraries || {}).length
                });
            }
            const componentObject = compilationResult.component.factory(this.runtimeContext, styles);
            this.registry.register(spec.name, componentObject, spec.namespace || namespace, version);
            return { success: true };
        }
        catch (error) {
            return {
                success: false,
                error: {
                    componentName: spec.name,
                    error: error instanceof Error ? error.message : String(error),
                    phase: 'registration'
                }
            };
        }
    }
    async registerChildComponents(children, options, registeredComponents, errors, warnings) {
        for (const child of children) {
            const childResult = await this.registerSingleComponent(child, {
                styles: options.styles,
                namespace: options.namespace,
                version: options.version,
                allowOverride: options.allowOverride,
                allLibraries: options.allLibraries
            });
            if (childResult.success) {
                if (child.code) {
                    registeredComponents.push(child.name);
                }
            }
            else {
                errors.push(childResult.error);
                if (!options.continueOnError) {
                    return;
                }
            }
            const nestedChildren = child.dependencies || [];
            if (nestedChildren.length > 0) {
                await this.registerChildComponents(nestedChildren, options, registeredComponents, errors, warnings);
            }
        }
    }
}
export async function registerComponentHierarchy(rootSpec, compiler, registry, runtimeContext, options) {
    const registrar = new ComponentHierarchyRegistrar(compiler, registry, runtimeContext);
    return registrar.registerHierarchy(rootSpec, options);
}
export function validateComponentSpec(spec) {
    const errors = [];
    if (!spec.name) {
        errors.push('Component specification must have a name');
    }
    if (spec.code) {
        if (typeof spec.code !== 'string') {
            errors.push(`Component code for ${spec.name} must be a string`);
        }
        if (spec.code.trim().length === 0) {
            errors.push(`Component code for ${spec.name} cannot be empty`);
        }
    }
    const children = spec.dependencies || [];
    children.forEach((child, index) => {
        const childErrors = validateComponentSpec(child);
        childErrors.forEach(error => {
            errors.push(`Child ${index} (${child.name || 'unnamed'}): ${error}`);
        });
    });
    return errors;
}
export function flattenComponentHierarchy(rootSpec) {
    const components = [rootSpec];
    const children = rootSpec.dependencies || [];
    children.forEach(child => {
        components.push(...flattenComponentHierarchy(child));
    });
    return components;
}
export function countComponentsInHierarchy(rootSpec, includeEmpty = false) {
    let count = 0;
    if (includeEmpty || rootSpec.code) {
        count = 1;
    }
    const children = rootSpec.dependencies || [];
    children.forEach(child => {
        count += countComponentsInHierarchy(child, includeEmpty);
    });
    return count;
}
//# sourceMappingURL=component-hierarchy.js.map