// 想要通过 MCP 的方式暴露 Opensumi 的 IDE 能力，就需要 Node.js 层打通 MCP 的通信
// 因为大部分 MCP 功能的实现在前端，因此需要再这里做前后端通信

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';

import { Autowired, Injectable } from '@opensumi/di';
import { RPCService } from '@opensumi/ide-connection';
import { ILogger } from '@opensumi/ide-core-common';
import { INodeLogger } from '@opensumi/ide-core-node';

import pkg from '../../../package.json';
import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend } from '../../common';
import { ImageCompressionOptions } from '../../common/image-compression';
import { IMCPServer, MCPServerDescription } from '../../common/mcp-server-manager';
import { IToolInvocationRegistryManager, ToolInvocationRegistryManager } from '../../common/tool-invocation-registry';
import { IMCPServerProxyService, IMCPToolResult, MCPTool, MCP_SERVER_TYPE } from '../../common/types';
import { MCPServerManagerImpl } from '../mcp-server-manager-impl';
import { SSEMCPServer } from '../mcp-server.sse';
import { StdioMCPServer } from '../mcp-server.stdio';

// 每个 BrowserTab 都对应了一个 SumiMCPServerBackend 实例
// SumiMCPServerBackend 需要做的事情：
// 维护 Browser 端工具的注册和调用
// 处理第三方 MCP Server 的注册和调用

@Injectable({ multiple: true })
export class SumiMCPServerBackend extends RPCService<IMCPServerProxyService> implements ISumiMCPServerBackend {
  // 这里需要考虑不同的 BrowserTab 的区分问题，目前的 POC 所有的 Tab 都会注册到 tools 中
  // 后续需要区分不同的 Tab 对应的实例
  private readonly mcpServerManager: MCPServerManagerImpl;

  @Autowired(ToolInvocationRegistryManager)
  private readonly toolInvocationRegistryManager: IToolInvocationRegistryManager;

  @Autowired(INodeLogger)
  private readonly logger: ILogger;

  private server: Server | undefined;

  // 对应 BrowserTab 的 clientId
  private clientId: string = '';

  constructor() {
    super();
    this.mcpServerManager = new MCPServerManagerImpl(this.toolInvocationRegistryManager, this.logger, this);
  }

  public setConnectionClientId(clientId: string) {
    this.clientId = clientId;
    this.mcpServerManager.setClientId(clientId);
  }

  async $compressToolResult(result: IMCPToolResult, options: ImageCompressionOptions) {
    if (!this.client) {
      throw new Error('SUMI MCP RPC Client not initialized');
    }
    return this.client.$compressToolResult(result, options);
  }

  async $getMCPTools() {
    if (!this.client) {
      throw new Error('SUMI MCP RPC Client not initialized');
    }
    // 获取 MCP 工具，这里的 client 为 MCPServerProxyService
    const tools = await this.client.$getBuiltinMCPTools();
    return tools;
  }

  $getMCPServerByName(name: string) {
    return this.mcpServerManager.getServerByName(name);
  }

  async callMCPTool(name: string, args: any) {
    if (!this.client) {
      throw new Error('SUMI MCP RPC Client not initialized');
    }
    return await this.client.$callMCPTool(name, args);
  }

  getServer() {
    return this.server;
  }

  // TODO 这里涉及到 Chat Stream Call 中带上 ClientID，具体方案需要进一步讨论
  async $getAllMCPTools(): Promise<MCPTool[]> {
    const registry = this.toolInvocationRegistryManager.getRegistry(this.clientId);
    return registry.getAllFunctions().map((tool) => ({
      name: tool.name || 'no-name',
      description: tool.description || 'no-description',
      inputSchema: tool.parameters,
      providerName: tool.providerName || 'no-provider-name',
    }));
  }

  public async $initBuiltinMCPServer(enabled: boolean) {
    const builtinMCPServer = new BuiltinMCPServer(this, this.logger);
    this.mcpServerManager.setClientId(this.clientId);
    await this.mcpServerManager.initBuiltinServer(builtinMCPServer, enabled);
    this.client?.$updateMCPServers();
  }

  public async $initExternalMCPServers(servers: MCPServerDescription[]) {
    this.mcpServerManager.setClientId(this.clientId);
    await this.mcpServerManager.addExternalMCPServers(servers);
    this.client?.$updateMCPServers();
  }

  async $initExposedMCPServer() {
    // 初始化 MCP Server
    this.server = new Server(
      {
        name: 'sumi-ide-mcp-server',
        version: pkg.version,
      },
      {
        capabilities: {
          tools: {},
        },
      },
    );

    // 设置工具列表请求处理器
    this.server.setRequestHandler(ListToolsRequestSchema, async (event) => {
      const tools = await this.$getMCPTools();
      return { tools };
    });

    // 设置工具调用请求处理器
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      try {
        const { name, arguments: args } = request.params;
        return await this.callMCPTool(name, args);
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          content: [{ type: 'text', text: `Error: ${errorMessage}` }],
          isError: true,
        };
      }
    });

    return this.server;
  }

  async $getServers() {
    const servers = Array.from(this.mcpServerManager.getServers().entries());
    const serverInfos = await Promise.all(
      servers.map(async ([serverName, server]) => {
        let tools: MCPTool[] = [];
        if (server.isStarted()) {
          // 只获取正在运行的 MCP Server 的工具列表
          const toolsResponse = await server.getTools();
          tools = toolsResponse.tools.map((tool) => ({
            name: tool.name,
            description: tool.description || '',
            inputSchema: tool.inputSchema,
            providerName: serverName,
          }));
        }

        // OpenSumi 内置的 MCP Server
        if (serverName === BUILTIN_MCP_SERVER_NAME) {
          return {
            name: server.getServerName(),
            isStarted: server.isStarted(),
            type: MCP_SERVER_TYPE.BUILTIN,
            tools,
          };
        }

        // 第三方 Stdio 类型的 MCP Server
        if (server instanceof StdioMCPServer) {
          return {
            name: server.getServerName(),
            isStarted: server.isStarted(),
            type: MCP_SERVER_TYPE.STDIO,
            command: server.command + ' ' + (server.args?.join(' ') || ''),
            tools,
          };
        } else if (server instanceof SSEMCPServer) {
          return {
            name: server.getServerName(),
            isStarted: server.isStarted(),
            type: MCP_SERVER_TYPE.SSE,
            url: server.url,
            tools,
          };
        }

        return {
          name: server.getServerName(),
          isStarted: server.isStarted(),
          type: '[MOCK] stdio',
          command: '[MOCK] npx sumi-ide-mcp-server',
          tools,
        };
      }),
    );

    // 将 builtin server 放在第一位
    const builtinServer = serverInfos.find((server) => server.name === BUILTIN_MCP_SERVER_NAME);
    const otherServers = serverInfos.filter((server) => server.name !== BUILTIN_MCP_SERVER_NAME);
    return builtinServer ? [builtinServer, ...otherServers] : otherServers;
  }

  async $startServer(serverName: string) {
    await this.mcpServerManager.startServer(serverName);
    this.client?.$updateMCPServers();
  }

  async $stopServer(serverName: string) {
    await this.mcpServerManager.stopServer(serverName);
    this.client?.$updateMCPServers();
  }

  public $addOrUpdateServer(description: MCPServerDescription) {
    this.mcpServerManager.updateShellPath();
    this.mcpServerManager.addOrUpdateServer(description);
  }

  public async $removeServer(name: string) {
    await this.mcpServerManager.removeServer(name);
  }

  public async $syncServer(name: string) {
    await this.mcpServerManager.syncServer(name);
  }
}

export const TokenBuiltinMCPServer = Symbol('TokenBuiltinMCPServer');

export class BuiltinMCPServer implements IMCPServer {
  private started: boolean = false;

  constructor(private readonly sumiMCPServer: SumiMCPServerBackend, private readonly logger: ILogger) {}

  isStarted(): boolean {
    return this.started;
  }

  getServerName(): string {
    return BUILTIN_MCP_SERVER_NAME;
  }

  getClient(): Client | null {
    return null;
  }

  async start(): Promise<void> {
    if (this.started) {
      return;
    }
    // TODO 考虑 MCP Server 的对外暴露
    // await this.sumiMCPServer.initMCPServer();
    this.started = true;
  }

  async callTool(toolName: string, toolCallId: string, arg_string: string): Promise<any> {
    if (!this.started) {
      throw new Error('MCP Server not started');
    }
    let args;
    try {
      args = JSON.parse(arg_string);
    } catch (error) {
      this.logger.error(
        `Failed to parse arguments for calling tool "${toolName}" in Builtin MCP server.
        Invalid JSON: ${arg_string}`,
        error,
      );
      throw error;
    }
    return this.sumiMCPServer.callMCPTool(toolName, {
      ...args,
      toolCallId,
    });
  }

  async getTools(): ReturnType<Client['listTools']> {
    if (!this.started) {
      throw new Error('MCP Server not started');
    }
    const tools = await this.sumiMCPServer.$getMCPTools();
    return { tools } as any;
  }

  update(_command: string, _args?: string[], _env?: { [key: string]: string }): void {
    // No-op for builtin server as it doesn't need command/args/env updates
  }

  stop(): void {
    if (!this.started) {
      return;
    }
    // No explicit cleanup needed for in-memory server
    this.started = false;
  }
}
