import { ComponentRegistryService } from './component-registry-service.js';
import { ComponentMetadataEngine } from '@memberjunction/core-entities';
export class ComponentResolver {
    constructor(registry, compiler, runtimeContext, debug = false) {
        this.registryService = null;
        this.compiler = null;
        this.runtimeContext = null;
        this.componentEngine = ComponentMetadataEngine.Instance;
        this.debug = false;
        this.registry = registry;
        this.resolverInstanceId = `resolver-${Date.now()}-${Math.random()}`;
        this.debug = debug;
        if (compiler && runtimeContext) {
            this.compiler = compiler;
            this.runtimeContext = runtimeContext;
            this.registryService = ComponentRegistryService.getInstance(compiler, runtimeContext, debug);
        }
    }
    async resolveComponents(spec, namespace = 'Global', contextUser) {
        console.log(`🚀 [ComponentResolver] Starting component resolution for: ${spec.name}`);
        console.log(`📋 [ComponentResolver] Root component spec:`, {
            name: spec.name,
            location: spec.location,
            registry: spec.registry,
            namespace: spec.namespace,
            hasCode: !!spec.code,
            hasDependencies: !!(spec.dependencies && spec.dependencies.length > 0)
        });
        if (this.debug) {
            console.log(`📋 [ComponentResolver] Dependencies to resolve:`, (spec.dependencies || []).map(d => ({
                name: d.name,
                location: d.location,
                namespace: d.namespace
            })));
        }
        const resolved = {};
        if (this.registryService) {
            if (this.debug) {
                console.log(`🔄 [ComponentResolver] Initializing component engine...`);
            }
            await this.componentEngine.Config(false, contextUser);
            if (this.debug) {
                console.log(`✅ [ComponentResolver] Component engine initialized`);
            }
        }
        await this.resolveComponentHierarchy(spec, resolved, namespace, new Set(), contextUser);
        if (!resolved[spec.name]) {
            console.error(`❌ [ComponentResolver] Root component '${spec.name}' was NOT added to resolved map!`);
            console.log(`📦 [ComponentResolver] What IS in resolved map:`, Object.keys(resolved));
        }
        if (this.debug) {
            console.log(`📊 [ComponentResolver] Resolved components before unwrapping:`, Object.keys(resolved));
        }
        const unwrapped = {};
        for (const [name, value] of Object.entries(resolved)) {
            if (value && typeof value === 'object' && 'component' in value) {
                if (typeof value.component === 'function') {
                    unwrapped[name] = value.component;
                    if (this.debug) {
                        console.log(`✅ [ComponentResolver] Unwrapped component: ${name} (was object with .component)`);
                    }
                }
                else {
                    console.error(`❌ [ComponentResolver] Component ${name} has invalid component property:`, typeof value.component, value);
                    unwrapped[name] = value;
                }
            }
            else if (typeof value === 'function') {
                unwrapped[name] = value;
                if (this.debug) {
                    console.log(`✅ [ComponentResolver] Component already a function: ${name}`);
                }
            }
            else {
                console.warn(`⚠️ [ComponentResolver] Component ${name} is not a function or wrapped component:`, typeof value, value);
                unwrapped[name] = value;
            }
        }
        if (this.debug) {
            console.log(`🎯 [ComponentResolver] Final resolved components:`, Object.keys(unwrapped).map(name => ({
                name,
                type: typeof unwrapped[name],
                isUndefined: unwrapped[name] === undefined
            })));
        }
        return unwrapped;
    }
    async resolveComponentHierarchy(spec, resolved, namespace, visited = new Set(), contextUser) {
        const componentId = `${spec.namespace || namespace}/${spec.name}@${spec.version || 'latest'}`;
        if (resolved[spec.name]) {
            if (this.debug) {
                console.log(`⏭️ [ComponentResolver] Component already resolved: ${spec.name}`);
            }
            return;
        }
        if (visited.has(componentId)) {
            if (this.debug) {
                console.warn(`Circular dependency detected for component: ${componentId}`);
            }
            return;
        }
        visited.add(componentId);
        if (this.debug) {
            console.log(`🔄 [ComponentResolver] Resolving dependencies for ${spec.name} BEFORE resolving itself`);
        }
        const children = spec.dependencies || [];
        for (const child of children) {
            if (this.debug) {
                console.log(`  ↳ [ComponentResolver] Resolving dependency: ${child.name} for parent ${spec.name}`);
            }
            await this.resolveComponentHierarchy(child, resolved, namespace, visited, contextUser);
        }
        if (children.length > 0 && this.debug) {
            console.log(`✅ [ComponentResolver] All ${children.length} dependencies resolved for ${spec.name}, now resolving itself`);
        }
        if (spec.location === 'registry' && this.registryService) {
            if (this.debug) {
                console.log(`🔍 [ComponentResolver] Looking for registry component: ${spec.name} in namespace: ${spec.namespace || namespace}`);
                if (spec.registry) {
                    console.log(`  📍 [ComponentResolver] External registry specified: ${spec.registry}`);
                }
                else {
                    console.log(`  📍 [ComponentResolver] Local registry (no registry field specified)`);
                }
            }
            try {
                if (spec.registry) {
                    if (this.debug) {
                        console.log(`🌐 [ComponentResolver] Fetching from external registry: ${spec.registry}`);
                    }
                    const compiledComponent = await this.registryService.getCompiledComponentFromRegistry(spec.registry, spec.namespace || namespace, spec.name, spec.version || 'latest', this.resolverInstanceId, contextUser);
                    if (compiledComponent) {
                        resolved[spec.name] = compiledComponent;
                        if (this.debug) {
                            console.log(`✅ [ComponentResolver] Successfully fetched and compiled from external registry: ${spec.name}`);
                        }
                    }
                    else {
                        console.error(`❌ [ComponentResolver] Failed to fetch from external registry: ${spec.name} from ${spec.registry}`);
                    }
                }
                else {
                    if (this.debug) {
                        console.log(`💾 [ComponentResolver] Looking for locally registered component: ${spec.name}`);
                    }
                    const component = await this.componentEngine.FindComponent(spec.name, spec.namespace || namespace, undefined, contextUser);
                    if (component) {
                        if (this.debug) {
                            console.log(`✅ [ComponentResolver] Found component in local DB:`, {
                                ID: component.ID,
                                Name: component.Name,
                                Namespace: component.Namespace,
                                Version: component.Version
                            });
                        }
                        const compiledComponent = await this.registryService.getCompiledComponent(component.ID, this.resolverInstanceId, contextUser);
                        resolved[spec.name] = compiledComponent;
                        if (this.debug) {
                            console.log(`📦 [ComponentResolver] Successfully compiled and resolved local component: ${spec.name}, type: ${typeof compiledComponent}`);
                        }
                    }
                    else {
                        console.error(`❌ [ComponentResolver] Local registry component NOT found in database: ${spec.name} with namespace: ${spec.namespace || namespace}`);
                    }
                }
            }
            catch (error) {
                if (this.debug) {
                    console.error(`Failed to load registry component ${spec.name}:`, error);
                }
            }
        }
        else {
            const componentNamespace = spec.namespace || namespace;
            if (spec.code && this.compiler) {
                if (this.debug) {
                    console.log(`🔨 [ComponentResolver] Component ${spec.name} has inline code, compiling...`);
                }
                try {
                    const compilationResult = await this.compiler.compile({
                        componentName: spec.name,
                        componentCode: spec.code,
                        libraries: spec.libraries,
                        dependencies: spec.dependencies,
                        allLibraries: []
                    });
                    if (compilationResult.success && compilationResult.component) {
                        if (!this.runtimeContext) {
                            console.error(`❌ [ComponentResolver] Cannot compile without runtime context`);
                            return;
                        }
                        const componentObject = compilationResult.component.factory(this.runtimeContext);
                        this.registry.register(spec.name, componentObject, componentNamespace, spec.version || 'latest');
                        resolved[spec.name] = componentObject;
                        if (this.debug) {
                            console.log(`✅ [ComponentResolver] Successfully compiled and registered inline component: ${spec.name}`);
                        }
                    }
                    else {
                        console.error(`❌ [ComponentResolver] Failed to compile inline component ${spec.name}:`, compilationResult.error);
                    }
                }
                catch (error) {
                    console.error(`❌ [ComponentResolver] Error compiling inline component ${spec.name}:`, error);
                }
            }
            else {
                if (this.debug) {
                    console.log(`🔍 [ComponentResolver] Looking for embedded component: ${spec.name} in namespace: ${componentNamespace}`);
                }
                const component = this.registry.get(spec.name, componentNamespace);
                if (component) {
                    resolved[spec.name] = component;
                    if (this.debug) {
                        console.log(`✅ [ComponentResolver] Found embedded component: ${spec.name}, type: ${typeof component}`);
                    }
                    if (this.debug) {
                        console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}, type:`, typeof component);
                    }
                }
                else {
                    if (this.debug) {
                        console.log(`⚠️ [ComponentResolver] Not found in namespace ${componentNamespace}, trying fallback namespace: ${namespace}`);
                    }
                    const fallbackComponent = this.registry.get(spec.name, namespace);
                    if (fallbackComponent) {
                        resolved[spec.name] = fallbackComponent;
                        if (this.debug) {
                            console.log(`✅ [ComponentResolver] Found embedded component in fallback namespace: ${spec.name}, type: ${typeof fallbackComponent}`);
                        }
                        if (this.debug) {
                            console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}, type:`, typeof fallbackComponent);
                        }
                    }
                    else {
                        console.error(`❌ [ComponentResolver] Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
                        if (this.debug) {
                            console.warn(`⚠️ Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
                        }
                        resolved[spec.name] = undefined;
                    }
                }
            }
        }
    }
    cleanup() {
        if (this.registryService) {
            if (this.debug) {
                console.log(`Cleaning up resolver: ${this.resolverInstanceId}`);
            }
        }
    }
    validateDependencies(spec, namespace = 'Global') {
        const missing = [];
        const checked = new Set();
        this.checkDependencies(spec, namespace, missing, checked);
        return missing;
    }
    checkDependencies(spec, namespace, missing, checked) {
        if (checked.has(spec.name))
            return;
        checked.add(spec.name);
        if (!this.registry.has(spec.name, namespace)) {
            missing.push(spec.name);
        }
        const children = spec.dependencies || [];
        for (const child of children) {
            this.checkDependencies(child, namespace, missing, checked);
        }
    }
    getDependencyGraph(spec) {
        const graph = new Map();
        const visited = new Set();
        this.buildDependencyGraph(spec, graph, visited);
        return graph;
    }
    buildDependencyGraph(spec, graph, visited) {
        if (visited.has(spec.name))
            return;
        visited.add(spec.name);
        const children = spec.dependencies || [];
        const dependencies = children.map(child => child.name);
        graph.set(spec.name, dependencies);
        for (const child of children) {
            this.buildDependencyGraph(child, graph, visited);
        }
    }
    getLoadOrder(spec) {
        const graph = this.getDependencyGraph(spec);
        const visited = new Set();
        const stack = [];
        for (const node of graph.keys()) {
            if (!visited.has(node)) {
                this.topologicalSortDFS(node, graph, visited, stack);
            }
        }
        return stack.reverse();
    }
    topologicalSortDFS(node, graph, visited, stack) {
        visited.add(node);
        const dependencies = graph.get(node) || [];
        for (const dep of dependencies) {
            if (!visited.has(dep)) {
                this.topologicalSortDFS(dep, graph, visited, stack);
            }
        }
        stack.push(node);
    }
    resolveInOrder(spec, namespace = 'Global') {
        const loadOrder = this.getLoadOrder(spec);
        const resolved = [];
        for (const name of loadOrder) {
            const component = this.registry.get(name, namespace);
            if (component) {
                resolved.push({ name, component });
            }
        }
        return resolved;
    }
    flattenComponentSpecs(spec) {
        const flattened = [];
        const visited = new Set();
        this.collectComponentSpecs(spec, flattened, visited);
        return flattened;
    }
    collectComponentSpecs(spec, collected, visited) {
        if (visited.has(spec.name))
            return;
        visited.add(spec.name);
        collected.push(spec);
        const children = spec.dependencies || [];
        for (const child of children) {
            this.collectComponentSpecs(child, collected, visited);
        }
    }
}
//# sourceMappingURL=component-resolver.js.map