import { v4 as uuidv4 } from "uuid";
import { log } from "./log";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
  createWebMcpModule,
  createMcpServerModule,
  McpServerConfig,
} from "./mcp";

export type McpModuleSource =
  | "builtin"
  | "user-dsl"
  | "external-http"
  | "external-stdio";

export type McpModule = {
  title: string;
  description: string;
  uuid: string;
  client: Client;
  source: McpModuleSource;
  config?: McpServerConfig; // 只有外部服务器模块才有配置
};

export class McpModulesManager {
  private modules: McpModule[] = [];

  public createMcpDslModule(
    { title, description }: { title: string; description: string },
    callback: (mcpServer: McpServer) => void
  ): McpModule {
    const client = createWebMcpModule(
      {
        title,
        description,
      },
      callback
    );

    this.modules.push({
      title,
      description,
      client,
      uuid: uuidv4(),
      source: "user-dsl",
    });

    return this.modules[this.modules.length - 1];
  }

  public createMcpBuiltinModule(
    { title, description }: { title: string; description: string },
    callback: (mcpServer: McpServer) => void
  ): McpModule {
    const client = createWebMcpModule(
      {
        title,
        description,
      },
      callback
    );

    this.modules.push({
      title,
      description,
      client,
      uuid: uuidv4(),
      source: "builtin",
    });

    return this.modules[this.modules.length - 1];
  }

  public async createMcpServerModule(
    config: McpServerConfig,
    uuid?: string
  ): Promise<McpModule> {
    const client = await createMcpServerModule(config);

    const moduleUuid = uuid || uuidv4();

    // 根据 serverUrl 判断连接类型
    const source: McpModuleSource =
      config.serverUrl.startsWith("http://") ||
      config.serverUrl.startsWith("https://") ||
      config.serverUrl.startsWith("ws://") ||
      config.serverUrl.startsWith("wss://")
        ? "external-http"
        : "external-stdio";

    this.modules.push({
      title: config.title,
      description: config.description,
      client: client,
      uuid: moduleUuid,
      source: source,
      config: config,
    });

    return this.modules[this.modules.length - 1];
  }

  public async removeMcpServerModule({
    uuid,
  }: {
    uuid: string;
  }): Promise<boolean> {
    try {
      const moduleIndex = this.modules.findIndex(
        (module) => module.uuid === uuid
      );
      if (moduleIndex === -1) {
        log.warn(`MCP Server Module with uuid ${uuid} not found`);
        return false;
      }

      const module = this.modules[moduleIndex];

      // 尝试断开连接
      try {
        if (module.client && typeof module.client.close === "function") {
          await module.client.close();
        }
      } catch (error) {
        log.warn(
          `Failed to close client for MCP server module ${uuid}:`,
          error
        );
      }

      // 从数组中移除模块
      this.modules.splice(moduleIndex, 1);

      log.info(
        `Successfully removed MCP server module: ${module.title} (${uuid})`
      );
      return true;
    } catch (error) {
      log.error(`Failed to remove MCP server module ${uuid}:`, error);
      return false;
    }
  }

  public async updateMcpServerModule({
    uuid,
    config,
  }: {
    uuid: string;
    config: McpServerConfig;
  }): Promise<McpModule | null> {
    try {
      const moduleIndex = this.modules.findIndex(
        (module) => module.uuid === uuid
      );
      if (moduleIndex === -1) {
        log.warn(`MCP Server Module with uuid ${uuid} not found`);
        return null;
      }

      const oldModule = this.modules[moduleIndex];

      // 关闭旧连接
      try {
        if (oldModule.client && typeof oldModule.client.close === "function") {
          await oldModule.client.close();
        }
      } catch (error) {
        log.warn(
          `Failed to close old client for MCP server module ${uuid}:`,
          error
        );
      }

      // 创建新的客户端连接
      const newClient = await createMcpServerModule(config);

      // 根据 serverUrl 判断连接类型
      const source: McpModuleSource =
        config.serverUrl.startsWith("http://") ||
        config.serverUrl.startsWith("https://") ||
        config.serverUrl.startsWith("ws://") ||
        config.serverUrl.startsWith("wss://")
          ? "external-http"
          : "external-stdio";

      // 更新模块
      this.modules[moduleIndex] = {
        title: config.title,
        description: config.description,
        client: newClient,
        uuid: uuid, // 保持原有的UUID
        source: source,
        config: config,
      };

      log.info(
        `Successfully updated MCP server module: ${config.title} (${uuid})`
      );
      return this.modules[moduleIndex];
    } catch (error) {
      log.error(`Failed to update MCP server module ${uuid}:`, error);
      return null;
    }
  }

  public listMcpModules(): McpModule[] {
    return this.modules;
  }

  public getModuleUuidsBySource(source: McpModuleSource): string[] {
    return this.modules
      .filter((module) => module.source === source)
      .map((module) => module.uuid);
  }

  public getMcpModule({ uuid }: { uuid?: string }): McpModule {
    const mod = this.modules.find((mcpModule) => mcpModule.uuid === uuid);
    if (!mod) {
      throw new Error(`McpModule with uuid ${uuid} not found`);
    }
    return mod;
  }

  async getTools({ uuids }: { uuids: string[] }) {
    const _tools: any[] = [];
    for (const uuid of uuids) {
      const { client } = this.getMcpModule({
        uuid,
      });
      try {
        const { tools } = await client.listTools();
        tools.forEach((tool) => {
          _tools.push({
            moduleId: uuid,
            ...tool,
          });
        });
      } catch (error: any) {
        // 如果是方法未找到错误(-32601)，继续处理其他模块
        if (error.code === -32601) {
          log.warn(`DSL Module ${uuid} does not support listTools method`);
        } else {
          // 其他错误重新抛出
          throw error;
        }
      }
    }
    return _tools;
  }

  async getResources({ uuids }: { uuids: string[] }) {
    const _resources: any[] = [];

    // 处理 DSL 模块
    for (const uuid of uuids) {
      if (uuid) {
        const { client } = this.getMcpModule({
          uuid: uuid,
        });
        try {
          const { resources } = await client.listResources();
          resources.forEach((resource) => {
            _resources.push({
              moduleId: uuid,
              ...resource,
            });
          });
        } catch (error: any) {
          // 如果是方法未找到错误(-32601)，继续处理其他模块
          if (error.code === -32601) {
            log.warn(
              `DSL Module ${uuid} does not support listResources method`
            );
          } else {
            // 其他错误重新抛出
            throw error;
          }
        }
      }
    }

    return _resources;
  }
}

export const mcpModulesManager = new McpModulesManager();
