/**
 * MCP client manager — composition root for the MCP subsystem.
 */
import { log } from '../../shared/logging/agent-log.js';
import { resolveMcpAuthStatus } from './auth-status.js';
import { buildCatalog, buildStatusLine } from './catalog.js';
import { loadMcpConfig } from './config.js';
import { closeConnection, connectServer, McpConnectionError } from './connection.js';
import { getHostedMcpOAuthProviderId } from './hosted-oauth-id.js';
import { registerHostedMcpOAuthProviders, unregisterHostedMcpOAuthProviders } from './hosted-oauth-provider.js';
import { createMcpDescribeTool } from './mcp-describe-tool.js';
import { createMcpExecuteTool } from './mcp-execute-tool.js';
import { isHostedServerConfig } from './types.js';
export async function createMcpManager(options) {
    const servers = loadMcpConfig({
        cwd: options.cwd,
        globalConfigDir: options.globalConfigDir
    });
    const serverNames = Object.keys(servers);
    if (serverNames.length === 0) {
        return null;
    }
    const hostedProviderIds = registerHostedMcpOAuthProviders(servers, options.authStorage, options.mcpOAuthStore);
    const connections = new Map();
    const failures = new Map();
    const failureKinds = new Map();
    const authStatuses = new Map();
    const results = await Promise.allSettled(serverNames.map(async (name) => {
        authStatuses.set(name, await resolveMcpAuthStatus(name, servers[name], {
            authStorage: options.authStorage,
            mcpOAuthStore: options.mcpOAuthStore,
            discovery: 'cached'
        }));
        const conn = await connectServer(name, servers[name], options.cwd, options.authStorage, options.mcpOAuthStore);
        return { name, conn };
    }));
    for (const [idx, result] of results.entries()) {
        if (result.status === 'fulfilled') {
            const config = servers[result.value.name];
            if (isHostedServerConfig(config)) {
                const reconnect = async () => {
                    if (!options.authStorage) {
                        return null;
                    }
                    const nextConnection = await connectServer(result.value.name, config, options.cwd, options.authStorage, options.mcpOAuthStore);
                    const previousConnection = connections.get(result.value.name);
                    connections.set(result.value.name, nextConnection);
                    nextConnection.reconnect = reconnect;
                    if (previousConnection && previousConnection !== nextConnection) {
                        closeConnection(previousConnection);
                    }
                    return nextConnection;
                };
                result.value.conn.reconnect = reconnect;
            }
            connections.set(result.value.name, result.value.conn);
        }
        else {
            const name = serverNames[idx];
            const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
            failures.set(name, reason);
            failureKinds.set(name, result.reason instanceof McpConnectionError ? result.reason.failureKind : 'connect_failed');
            log.warn('mcp', `MCP server '${name}' failed to start: ${reason}`);
        }
    }
    const catalog = buildCatalog([...connections.values()]);
    const tools = connections.size > 0
        ? [createMcpExecuteTool(connections, catalog), createMcpDescribeTool(connections)]
        : [];
    const statusLine = buildStatusLine(serverNames.map((name) => {
        const connection = connections.get(name);
        const rawAuthStatus = authStatuses.get(name) ?? 'unsupported';
        const authStatus = failureKinds.get(name) === 'auth_required'
            ? 'not_logged_in'
            : connection && rawAuthStatus === 'not_logged_in'
                ? 'unsupported'
                : rawAuthStatus;
        return {
            name,
            connected: connection,
            failure: failures.get(name),
            failureKind: failureKinds.get(name),
            authStatus,
            transport: isHostedServerConfig(servers[name]) ? 'streamable_http' : 'stdio'
        };
    }));
    if (statusLine) {
        log.info('mcp', statusLine);
    }
    const serverStatuses = serverNames.map((name) => {
        const connection = connections.get(name);
        const failure = failures.get(name);
        const config = servers[name];
        const rawAuthStatus = authStatuses.get(name) ?? 'unsupported';
        const authStatus = failureKinds.get(name) === 'auth_required'
            ? 'not_logged_in'
            : connection && rawAuthStatus === 'not_logged_in'
                ? 'unsupported'
                : rawAuthStatus;
        return {
            name,
            status: connection ? 'connected' : 'failed',
            transport: isHostedServerConfig(config) ? 'streamable_http' : 'stdio',
            authStatus,
            ...(failure ? { failureKind: failureKinds.get(name) ?? 'connect_failed' } : {}),
            ...(isHostedServerConfig(config) && (authStatus === 'oauth' || authStatus === 'not_logged_in')
                ? {
                    oauthProviderId: getHostedMcpOAuthProviderId(name, config),
                    oauthProviderName: config.oauth?.providerName ?? `MCP: ${name}`
                }
                : {}),
            toolCount: connection?.tools.length ?? 0,
            toolNames: connection?.tools.map((t) => t.name) ?? [],
            ...(failure ? { error: failure } : {})
        };
    });
    return {
        tools,
        statusLine,
        globalConfigDir: options.globalConfigDir,
        servers: serverStatuses,
        shutdown() {
            for (const conn of connections.values()) {
                closeConnection(conn);
            }
            connections.clear();
            unregisterHostedMcpOAuthProviders(hostedProviderIds);
        }
    };
}
//# sourceMappingURL=index.js.map