import { setLiveMcpStateGetter } from "./state-getter.js";
import { Type } from "typebox";
import { showTools, reconnectServers, authenticateServer, showSetupStatus } from "./commands.js";
import { loadMcpConfig } from "./config.js";
import { buildProxyDescription, createDirectToolExecutor, getMissingConfiguredDirectToolServers, resolveDirectTools } from "./direct-tools.js";
import { flushMetadataCache, initializeMcp, updateStatusBar } from "./init.js";
import { loadMetadataCache } from "./metadata-cache.js";
import { executeCall, executeConnect, executeDescribe, executeList, executeSearch, executeStatus, executeUiMessages } from "./proxy-modes.js";
import { getConfigPathFromArgv, truncateAtWord } from "./utils.js";
import { initializeOAuth, shutdownOAuth } from "./mcp-auth-flow.js";
export default function mcpAdapter(ext) {
    let state = null;
    setLiveMcpStateGetter(() => state);
    let initPromise = null;
    let lifecycleGeneration = 0;
    async function shutdownState(currentState, reason) {
        if (!currentState)
            return;
        if (currentState.uiServer) {
            currentState.uiServer.close(reason);
            currentState.uiServer = null;
        }
        let flushError;
        try {
            flushMetadataCache(currentState);
        }
        catch (error) {
            flushError = error;
        }
        try {
            await currentState.lifecycle.gracefulShutdown();
        }
        catch (error) {
            if (flushError) {
                console.error("MCP: graceful shutdown failed after metadata flush error", error);
            }
            else {
                throw error;
            }
        }
        if (flushError) {
            throw flushError;
        }
    }
    const earlyConfigPath = getConfigPathFromArgv();
    const earlyConfig = loadMcpConfig(earlyConfigPath);
    const earlyCache = loadMetadataCache();
    const prefix = earlyConfig.settings?.toolPrefix ?? "server";
    const envRaw = process.env.MCP_DIRECT_TOOLS;
    const directSpecs = envRaw === "__none__"
        ? []
        : resolveDirectTools(earlyConfig, earlyCache, prefix, envRaw?.split(",").map(s => s.trim()).filter(Boolean));
    const missingConfiguredDirectToolServers = getMissingConfiguredDirectToolServers(earlyConfig, earlyCache);
    const shouldRegisterProxyTool = earlyConfig.settings?.disableProxyTool !== true
        || directSpecs.length === 0
        || missingConfiguredDirectToolServers.length > 0;
    for (const spec of directSpecs) {
        ext.registerTool({
            name: spec.prefixedName,
            label: `MCP: ${spec.originalName}`,
            description: spec.description || "(no description)",
            promptSnippet: truncateAtWord(spec.description, 100) || `MCP tool from ${spec.serverName}`,
            parameters: Type.Unsafe(spec.inputSchema || { type: "object", properties: {} }),
            execute: createDirectToolExecutor(() => state, () => initPromise, spec),
        });
    }
    const getSpectralTools = () => ext.getAllTools();
    ext.registerFlag("mcp-config", {
        description: "Path to MCP config file",
        type: "string",
    });
    ext.on("session_start", async (_event, ctx) => {
        const generation = ++lifecycleGeneration;
        const previousState = state;
        state = null;
        initPromise = null;
        try {
            await Promise.all([
                shutdownState(previousState, "session_restart"),
                shutdownOAuth(),
            ]);
        }
        catch (error) {
            console.error("MCP: failed to shut down previous session state", error);
        }
        if (generation !== lifecycleGeneration) {
            return;
        }
        await initializeOAuth().catch(err => {
            console.error("MCP OAuth initialization failed:", err);
        });
        const promise = initializeMcp(ext, ctx);
        initPromise = promise;
        promise.then(async (nextState) => {
            if (generation !== lifecycleGeneration || initPromise !== promise) {
                try {
                    await shutdownState(nextState, "stale_session_start");
                }
                catch (error) {
                    console.error("MCP: failed to clean stale session state", error);
                }
                return;
            }
            state = nextState;
            updateStatusBar(nextState);
            initPromise = null;
        }).catch(err => {
            if (generation !== lifecycleGeneration) {
                return;
            }
            if (initPromise !== promise && initPromise !== null) {
                return;
            }
            console.error("MCP initialization failed:", err);
            initPromise = null;
        });
    });
    ext.on("session_shutdown", async () => {
        ++lifecycleGeneration;
        const currentState = state;
        state = null;
        initPromise = null;
        try {
            await Promise.all([
                shutdownState(currentState, "session_shutdown"),
                shutdownOAuth(),
            ]);
        }
        catch (error) {
            console.error("MCP: session shutdown cleanup failed", error);
        }
    });
    ext.registerCommand("mcp", {
        description: "Show MCP server status",
        handler: async (args, ctx) => {
            if (!state && initPromise) {
                try {
                    state = await initPromise;
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    if (ctx.hasUI)
                        ctx.ui.notify(`MCP initialization failed: ${message}`, "error");
                    return;
                }
            }
            if (!state) {
                if (ctx.hasUI)
                    ctx.ui.notify("MCP not initialized", "error");
                return;
            }
            const parts = args?.trim()?.split(/\s+/) ?? [];
            const subcommand = parts[0] ?? "";
            const targetServer = parts[1];
            switch (subcommand) {
                case "reconnect":
                    await reconnectServers(state, ctx, targetServer);
                    break;
                case "tools":
                    await showTools(state, ctx);
                    break;
                case "setup":
                    await showSetupStatus(state, ext, ctx, earlyConfigPath);
                    break;
                case "status":
                case "":
                default:
                    await showSetupStatus(state, ext, ctx, earlyConfigPath);
                    break;
            }
        },
    });
    ext.registerCommand("mcp-auth", {
        description: "Authenticate with an MCP server (OAuth)",
        handler: async (args, ctx) => {
            const serverName = args?.trim();
            if (!serverName) {
                if (ctx.hasUI)
                    ctx.ui.notify("Usage: /mcp-auth <server-name>", "error");
                return;
            }
            if (!state && initPromise) {
                try {
                    state = await initPromise;
                }
                catch (error) {
                    const message = error instanceof Error ? error.message : String(error);
                    if (ctx.hasUI)
                        ctx.ui.notify(`MCP initialization failed: ${message}`, "error");
                    return;
                }
            }
            if (!state) {
                if (ctx.hasUI)
                    ctx.ui.notify("MCP not initialized", "error");
                return;
            }
            await authenticateServer(serverName, state.config, ctx);
        },
    });
    if (shouldRegisterProxyTool) {
        ext.registerTool({
            name: "mcp",
            label: "MCP",
            description: buildProxyDescription(earlyConfig, earlyCache, directSpecs),
            promptSnippet: "MCP gateway - connect to MCP servers and call their tools",
            parameters: Type.Object({
                tool: Type.Optional(Type.String({ description: "Tool name to call (e.g., 'xcodebuild_list_sims')" })),
                args: Type.Optional(Type.String({ description: "Arguments as JSON string (e.g., '{\"key\": \"value\"}')" })),
                connect: Type.Optional(Type.String({ description: "Server name to connect (closes any stale connection first, forces fresh reconnect)" })),
                describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
                search: Type.Optional(Type.String({ description: "Search tools by name/description" })),
                regex: Type.Optional(Type.Boolean({ description: "Treat search as regex (default: substring match)" })),
                includeSchemas: Type.Optional(Type.Boolean({ description: "Include parameter schemas in search results (default: true)" })),
                server: Type.Optional(Type.String({ description: "Filter to specific server (also disambiguates tool calls)" })),
                action: Type.Optional(Type.String({ description: "Action: 'ui-messages' to retrieve prompts/intents from UI sessions" })),
                restart: Type.Optional(Type.String({ description: "Force restart a dead server process. Use when server returns 'Not connected' or calls fail." })),
            }),
            async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
                let parsedArgs;
                if (params.args) {
                    try {
                        parsedArgs = JSON.parse(params.args);
                        if (typeof parsedArgs !== "object" || parsedArgs === null || Array.isArray(parsedArgs)) {
                            const gotType = Array.isArray(parsedArgs) ? "array" : parsedArgs === null ? "null" : typeof parsedArgs;
                            throw new Error(`Invalid args: expected a JSON object, got ${gotType}`);
                        }
                    }
                    catch (error) {
                        if (error instanceof SyntaxError) {
                            throw new Error(`Invalid args JSON: ${error.message}`, { cause: error });
                        }
                        throw error;
                    }
                }
                if (!state && initPromise) {
                    try {
                        state = await initPromise;
                    }
                    catch (error) {
                        const message = error instanceof Error ? error.message : String(error);
                        return {
                            content: [{ type: "text", text: `MCP initialization failed: ${message}` }],
                            details: { error: "init_failed", message },
                        };
                    }
                }
                if (!state) {
                    return {
                        content: [{ type: "text", text: "MCP not initialized" }],
                        details: { error: "not_initialized" },
                    };
                }
                if (params.action === "ui-messages") {
                    return executeUiMessages(state);
                }
                if (params.tool) {
                    return executeCall(state, params.tool, parsedArgs, params.server, getSpectralTools);
                }
                if (params.restart) {
                    return executeConnect(state, params.restart);
                }
                if (params.connect) {
                    return executeConnect(state, params.connect);
                }
                if (params.describe) {
                    return executeDescribe(state, params.describe);
                }
                if (params.search) {
                    return executeSearch(state, params.search, params.regex, params.server, params.includeSchemas);
                }
                if (params.server) {
                    return executeList(state, params.server);
                }
                return executeStatus(state);
            },
        });
    }
}
