import { resolvePath } from "../utils/paths.js";
import { yieldToEventLoop } from "../utils/event-loop.js";
import { startTimingSpan, endTimingSpan } from "./timings.js";
import { loadExtensionFromFactory, loadExtensionsCached, } from "./extensions/loader.js";
import { resourceInternals } from "./resource-loader-internals.js";
function resolveExtensionLoadPath(loader, path) {
    return resolvePath(path, resourceInternals(loader).cwd, { normalizeUnicodeSpaces: true });
}
export async function loadFinalExtensionSet(loader, extensionPaths, preTrustExtensions, workflowResourceProvider, inheritanceSnapshotProvider) {
    const state = resourceInternals(loader);
    if (!preTrustExtensions) {
        const loadExtensionsSpan = startTimingSpan("DefaultResourceLoader.reload.loadExtensions");
        const extensionsResult = await loadExtensionsCached(extensionPaths, state.cwd, state.eventBus, workflowResourceProvider, undefined, inheritanceSnapshotProvider);
        endTimingSpan(loadExtensionsSpan);
        const inlineExtensionsSpan = startTimingSpan("DefaultResourceLoader.reload.loadInlineExtensionFactories");
        const inlineExtensions = await loadExtensionFactories(loader, extensionsResult.runtime, workflowResourceProvider, inheritanceSnapshotProvider);
        endTimingSpan(inlineExtensionsSpan);
        extensionsResult.extensions.push(...inlineExtensions.extensions);
        extensionsResult.errors.push(...inlineExtensions.errors);
        addExtensionConflictDiagnostics(extensionsResult);
        return extensionsResult;
    }
    const preloadedByPath = new Map(preTrustExtensions.extensions
        .filter((extension) => !extension.path.startsWith("<inline:"))
        .map((extension) => [extension.resolvedPath, extension]));
    const failedPreloadPaths = new Set(preTrustExtensions.errors.map((error) => resolveExtensionLoadPath(loader, error.path)));
    const remainingPaths = extensionPaths.filter((path) => {
        const resolvedPath = resolveExtensionLoadPath(loader, path);
        return !preloadedByPath.has(resolvedPath) && !failedPreloadPaths.has(resolvedPath);
    });
    const loadExtensionsSpan = startTimingSpan("DefaultResourceLoader.reload.loadExtensions");
    const remainingExtensions = await loadExtensionsCached(remainingPaths, state.cwd, state.eventBus, workflowResourceProvider, preTrustExtensions.runtime, inheritanceSnapshotProvider);
    endTimingSpan(loadExtensionsSpan);
    const loadedByPath = new Map(preloadedByPath);
    for (const extension of remainingExtensions.extensions) {
        loadedByPath.set(extension.resolvedPath, extension);
    }
    const inlineExtensions = preTrustExtensions.extensions.filter((extension) => extension.path.startsWith("<inline:"));
    const orderedExtensions = extensionPaths
        .map((path) => loadedByPath.get(resolveExtensionLoadPath(loader, path)))
        .filter((extension) => extension !== undefined);
    orderedExtensions.push(...inlineExtensions);
    const extensionsResult = {
        extensions: orderedExtensions,
        errors: [...preTrustExtensions.errors, ...remainingExtensions.errors],
        runtime: preTrustExtensions.runtime,
    };
    addExtensionConflictDiagnostics(extensionsResult);
    return extensionsResult;
}
export async function loadExtensionFactories(loader, runtime, workflowResourceProvider, inheritanceSnapshotProvider) {
    const state = resourceInternals(loader);
    const extensions = [];
    const errors = [];
    for (const [index, factory] of state.extensionFactories.entries()) {
        if (index > 0) {
            await yieldToEventLoop();
        }
        const extensionPath = `<inline:${index + 1}>`;
        try {
            const extension = await loadExtensionFromFactory(factory, state.cwd, state.eventBus, runtime, extensionPath, workflowResourceProvider, inheritanceSnapshotProvider);
            extensions.push(extension);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : "failed to load extension";
            errors.push({ path: extensionPath, error: message });
        }
    }
    return { extensions, errors };
}
function addExtensionConflictDiagnostics(extensionsResult) {
    // Detect extension conflicts (tools, commands, flags with same names from different extensions)
    // Keep all extensions loaded. Conflicts are reported as diagnostics, and precedence is handled by load order.
    const conflicts = detectExtensionConflicts(extensionsResult.extensions);
    for (const conflict of conflicts) {
        extensionsResult.errors.push({ path: conflict.path, error: conflict.message });
    }
}
function detectExtensionConflicts(extensions) {
    const conflicts = [];
    // Track which extension registered each tool and flag
    const toolOwners = new Map();
    const flagOwners = new Map();
    for (const ext of extensions) {
        // Check tools
        for (const toolName of ext.tools.keys()) {
            const existingOwner = toolOwners.get(toolName);
            if (existingOwner && existingOwner !== ext.path) {
                conflicts.push({
                    path: ext.path,
                    message: `Tool "${toolName}" conflicts with ${existingOwner}`,
                });
            }
            else {
                toolOwners.set(toolName, ext.path);
            }
        }
        // Check flags
        for (const flagName of ext.flags.keys()) {
            const existingOwner = flagOwners.get(flagName);
            if (existingOwner && existingOwner !== ext.path) {
                conflicts.push({
                    path: ext.path,
                    message: `Flag "--${flagName}" conflicts with ${existingOwner}`,
                });
            }
            else {
                flagOwners.set(flagName, ext.path);
            }
        }
    }
    return conflicts;
}
//# sourceMappingURL=resource-loader-extensions.js.map