import { ensureCodeModeHostBinary } from "./binary.js";
import { CodeModeHostClient } from "./host-client.js";
export class SharedCodeModeRuntime {
    providers = new Map();
    clientPromise;
    clientStartupAbort;
    addProvider(provider) {
        const id = {};
        this.providers.set(id, provider);
        return id;
    }
    removeProvider(id) {
        this.providers.delete(id);
    }
    activeProviders(ctx) {
        return [...this.providers.values()].filter((provider) => !provider.isActive || provider.isActive(ctx));
    }
    collectTools(ctx) {
        return collectUniqueTools(this.activeProviders(ctx), ctx);
    }
    collectRenderTools() {
        return collectUniqueTools([...this.providers.values()].filter((provider) => provider.providesRenderers));
    }
    useRichRendering() {
        return [...this.providers.values()].find((provider) => provider.richRendering)
            ?.richRendering?.() ?? true;
    }
    async getClient() {
        if (!this.clientPromise) {
            const startupAbort = new AbortController();
            const pending = ensureCodeModeHostBinary(startupAbort.signal).then((binary) => new CodeModeHostClient({ binary, tools: [] }));
            this.clientPromise = pending;
            this.clientStartupAbort = startupAbort;
            void pending.then(() => {
                if (this.clientPromise === pending)
                    this.clientStartupAbort = undefined;
            }, () => {
                if (this.clientPromise !== pending)
                    return;
                this.clientPromise = undefined;
                this.clientStartupAbort = undefined;
            });
        }
        return this.clientPromise;
    }
    prepare(ctx) {
        if (this.activeProviders(ctx).length === 0)
            return undefined;
        return this.getClient().then(() => undefined);
    }
    async shutdownHost() {
        while (this.clientPromise) {
            const pending = this.clientPromise;
            this.clientPromise = undefined;
            this.clientStartupAbort?.abort();
            this.clientStartupAbort = undefined;
            try {
                await (await pending).shutdown();
            }
            catch {
                // Startup failure already reached the caller.
            }
        }
    }
}
function collectUniqueTools(providers, ctx) {
    const tools = providers.flatMap((provider) => provider.getTools(ctx));
    const byName = new Map();
    const unique = [];
    for (const tool of tools) {
        const previous = byName.get(tool.name);
        if (previous) {
            if ("sourcePath" in previous &&
                "sourcePath" in tool &&
                previous.sourcePath === tool.sourcePath)
                continue;
            throw new Error(`Duplicate code-mode tool: ${tool.name}`);
        }
        byName.set(tool.name, tool);
        unique.push(tool);
    }
    return unique;
}
