import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import { readFileSync, existsSync } from "node:fs";
import { createMcpDependencies } from "../main/mcpDependencies.js";
import { ReviewContextFileSystemGateway } from "../modules/review-execution/interface-adapters/gateways/reviewContext.fileSystem.gateway.js";
import { createGetWorkflowHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/getWorkflow.handler.js";
import { createStartAgentHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/startAgent.handler.js";
import { createCompleteAgentHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/completeAgent.handler.js";
import { createSetPhaseHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/setPhase.handler.js";
import { createGetThreadsHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/getThreads.handler.js";
import { createAddActionHandler } from "../modules/review-execution/interface-adapters/controllers/mcp/addAction.handler.js";
import { FileSystemMcpCompletionBridge } from "../modules/claude-invocation/interface-adapters/gateways/mcpCompletion.fileSystem.gateway.js";
import { getProjectAgents, getFollowupAgents } from "../config/projectConfig.js";
import { getJobContextFilePath } from "../shared/services/mcpJobContext.js";
import { mcpLogger } from "./mcpLogger.js";
export function loadJobContextFromFile(jobId) {
    try {
        const filePath = getJobContextFilePath(jobId);
        if (!existsSync(filePath)) {
            mcpLogger.debug("No per-job context file found", { jobId, path: filePath });
            return null;
        }
        const content = readFileSync(filePath, "utf-8");
        const data = JSON.parse(content);
        mcpLogger.info("Loaded job context from per-job file", { jobId, path: filePath });
        return {
            jobId: data.jobId,
            localPath: data.localPath,
            mergeRequestId: data.mergeRequestId,
            jobType: data.jobType || "review",
        };
    }
    catch (error) {
        mcpLogger.error("Failed to read per-job context file", { jobId, error: String(error) });
        return null;
    }
}
export function getJobContextFromEnv() {
    const jobId = process.env.MCP_JOB_ID;
    const localPath = process.env.MCP_LOCAL_PATH;
    const mergeRequestId = process.env.MCP_MERGE_REQUEST_ID;
    const jobType = process.env.MCP_JOB_TYPE || "review";
    if (!jobId || !localPath || !mergeRequestId) {
        return null;
    }
    return { jobId, localPath, mergeRequestId, jobType };
}
export function ensureJobContextLoaded(jobId, mcpDeps) {
    if (mcpDeps.jobContextGateway.get(jobId)) {
        return;
    }
    const jobContext = getJobContextFromEnv() ?? loadJobContextFromFile(jobId);
    if (!jobContext) {
        mcpLogger.warn("No job context found for lazy loading", { jobId });
        return;
    }
    mcpDeps.jobContextGateway.register(jobContext.jobId, {
        localPath: jobContext.localPath,
        mergeRequestId: jobContext.mergeRequestId,
    });
    mcpLogger.info("Job context lazy-loaded and registered", { jobId });
    const agents = jobContext.jobType === "followup"
        ? getFollowupAgents(jobContext.localPath)
        : getProjectAgents(jobContext.localPath);
    const agentNames = agents?.map((a) => a.name) ?? ["analysis"];
    mcpDeps.progressGateway.createProgress(jobContext.jobId, agentNames);
    mcpLogger.info("Progress created via lazy loading", { jobId, agentNames });
}
const TOOL_DEFINITIONS = [
    {
        name: "get_workflow",
        description: "Get the current workflow state including agents and their status",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
            },
            required: ["jobId"],
        },
    },
    {
        name: "start_agent",
        description: "Signal that an agent audit is starting",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
                agentName: { type: "string", description: "The name of the agent starting" },
            },
            required: ["jobId", "agentName"],
        },
    },
    {
        name: "complete_agent",
        description: "Signal that an agent audit is complete",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
                agentName: { type: "string", description: "The name of the agent completing" },
                status: { type: "string", enum: ["success", "failed"], description: "The completion status" },
                error: { type: "string", description: "Error message if status is failed" },
            },
            required: ["jobId", "agentName", "status"],
        },
    },
    {
        name: "set_phase",
        description: "Set the current review phase",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
                phase: {
                    type: "string",
                    enum: ["initializing", "agents-running", "synthesizing", "publishing", "completed"],
                    description: "The phase to set",
                },
            },
            required: ["jobId", "phase"],
        },
    },
    {
        name: "get_threads",
        description: "Get all discussion threads from the MR/PR",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
            },
            required: ["jobId"],
        },
    },
    {
        name: "add_action",
        description: "Add an action to be executed (resolve thread, post comment, reply, inline comment)",
        inputSchema: {
            type: "object",
            properties: {
                jobId: { type: "string", description: "The job ID for the review" },
                type: {
                    type: "string",
                    enum: ["THREAD_RESOLVE", "THREAD_REPLY", "POST_COMMENT", "POST_INLINE_COMMENT"],
                    description: "The action type",
                },
                threadId: { type: "string", description: "Thread ID for resolve/reply actions" },
                message: { type: "string", description: "Message for reply action" },
                body: { type: "string", description: "Body for post comment action or inline comment" },
                filePath: { type: "string", description: "File path for inline comment (POST_INLINE_COMMENT)" },
                line: { type: "number", description: "Line number for inline comment (POST_INLINE_COMMENT)" },
            },
            required: ["jobId", "type"],
        },
    },
];
export async function startMcpServer() {
    mcpLogger.info("=== MCP Server Starting ===", {
        logFile: mcpLogger.getLogPath(),
        pid: process.pid,
    });
    const reviewContextGateway = new ReviewContextFileSystemGateway();
    const mcpDeps = createMcpDependencies({ reviewContextGateway });
    mcpLogger.info("MCP server initialized - context will be lazy-loaded on first tool call");
    const getWorkflowHandler = createGetWorkflowHandler({ progressGateway: mcpDeps.progressGateway });
    const startAgentHandler = createStartAgentHandler({ progressGateway: mcpDeps.progressGateway });
    const completeAgentHandler = createCompleteAgentHandler({ progressGateway: mcpDeps.progressGateway });
    // FileSystem-backed bridge: publishes a completion event to ~/.claude-review/
    // completions/<jobId>.json so the Fastify host's awaitSessionCompletion can
    // observe it cross-process. The MCP server lives in a sub-process spawned
    // by `claude --bg`; an in-memory bridge cannot cross that boundary.
    const completionBridge = new FileSystemMcpCompletionBridge();
    const setPhaseHandler = createSetPhaseHandler({
        progressGateway: mcpDeps.progressGateway,
        completionBridge,
    });
    const getThreadsHandler = createGetThreadsHandler({
        jobContextGateway: mcpDeps.jobContextGateway,
        reviewContextGateway: mcpDeps.reviewContextGateway,
    });
    const addActionHandler = createAddActionHandler({
        jobContextGateway: mcpDeps.jobContextGateway,
        reviewContextGateway: mcpDeps.reviewContextGateway,
    });
    const handlers = {
        get_workflow: getWorkflowHandler,
        start_agent: startAgentHandler,
        complete_agent: completeAgentHandler,
        set_phase: setPhaseHandler,
        get_threads: getThreadsHandler,
        add_action: addActionHandler,
    };
    const server = new Server({
        name: "claude-review-progress",
        version: "1.0.0",
    }, {
        capabilities: {
            tools: {},
        },
    });
    server.setRequestHandler(ListToolsRequestSchema, async () => {
        mcpLogger.debug("ListTools called", { toolCount: TOOL_DEFINITIONS.length });
        return { tools: TOOL_DEFINITIONS };
    });
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
        const { name, arguments: args } = request.params;
        const startTime = Date.now();
        mcpLogger.info(`Tool called: ${name}`, { args });
        const jobId = args?.jobId;
        if (jobId) {
            ensureJobContextLoaded(jobId, mcpDeps);
        }
        const handler = handlers[name];
        if (!handler) {
            mcpLogger.error(`Unknown tool: ${name}`);
            return {
                content: [{ type: "text", text: `Unknown tool: ${name}` }],
                isError: true,
            };
        }
        try {
            const result = handler(args ?? {});
            const durationMs = Date.now() - startTime;
            mcpLogger.info(`Tool completed: ${name}`, {
                durationMs,
                isError: result.isError ?? false,
                responsePreview: result.content?.[0]?.text?.substring(0, 200),
            });
            return result;
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            mcpLogger.error(`Tool error: ${name}`, { error: errorMessage });
            return {
                content: [{ type: "text", text: `Error: ${errorMessage}` }],
                isError: true,
            };
        }
    });
    const transport = new StdioServerTransport();
    mcpLogger.info("Connecting to stdio transport...");
    await server.connect(transport);
    mcpLogger.info("MCP Server connected and ready");
}
if (import.meta.url === `file://${process.argv[1]}`) {
    startMcpServer().catch(console.error);
}
//# sourceMappingURL=mcpServerStdio.js.map