"""
MaiSaka - MCP 管理器
管理所有 MCP 服务器连接，提供统一的工具发现与调用接口。
"""

from typing import Optional

from ..config import console
from .config import MCPServerConfig, load_mcp_config
from .connection import MCPConnection, MCP_AVAILABLE

# 内置工具名称集合 —— MCP 工具不允许与这些名称冲突
BUILTIN_TOOL_NAMES = frozenset(
    {
        "reply",
        "no_reply",
        "wait",
        "stop",
        "write_file",
        "read_file",
        "list_files",
        "create_table",
        "list_tables",
        "view_table",
    }
)


class MCPManager:
    """
    MCP 服务器连接管理器。

    职责：
    - 根据配置文件连接所有 MCP 服务器
    - 将 MCP 工具转换为 OpenAI function calling 格式
    - 路由工具调用到正确的 MCP 服务器
    - 统一管理连接生命周期
    """

    def __init__(self):
        self._connections: dict[str, MCPConnection] = {}  # server_name → connection
        self._tool_to_server: dict[str, str] = {}  # tool_name → server_name

    # ──────── 工厂方法 ────────

    @classmethod
    async def from_config(
        cls,
        config_path: str = "mcp_config.json",
    ) -> Optional["MCPManager"]:
        """
        从配置文件创建并初始化 MCPManager。

        Args:
            config_path: mcp_config.json 文件路径

        Returns:
            初始化完成的 MCPManager；无配置或全部连接失败时返回 None。
        """
        configs = load_mcp_config(config_path)
        if not configs:
            return None

        if not MCP_AVAILABLE:
            console.print("[warning]⚠️ 发现 MCP 配置但未安装 mcp SDK，请运行: pip install mcp[/warning]")
            return None

        manager = cls()
        await manager._connect_all(configs)

        if not manager._connections:
            console.print("[warning]⚠️ 所有 MCP 服务器连接失败[/warning]")
            return None

        return manager

    # ──────── 连接管理 ────────

    async def _connect_all(self, configs: list[MCPServerConfig]):
        """连接所有配置的 MCP 服务器，跳过失败的连接。"""
        for cfg in configs:
            conn = MCPConnection(cfg)
            success = await conn.connect()
            if not success:
                continue

            self._connections[cfg.name] = conn

            # 注册工具，检查冲突
            registered = 0
            for tool in conn.tools:
                tool_name = tool.name

                if tool_name in BUILTIN_TOOL_NAMES:
                    console.print(
                        f"[warning]⚠️ MCP 工具 '{tool_name}' (来自 {cfg.name}) 与内置工具冲突，已跳过[/warning]"
                    )
                    continue

                if tool_name in self._tool_to_server:
                    existing_server = self._tool_to_server[tool_name]
                    console.print(
                        f"[warning]⚠️ MCP 工具 '{tool_name}' "
                        f"(来自 {cfg.name}) 与 {existing_server} 冲突，已跳过[/warning]"
                    )
                    continue

                self._tool_to_server[tool_name] = cfg.name
                registered += 1

            console.print(
                f"[success]✓ MCP 服务器 '{cfg.name}' 已连接[/success] [muted]({registered} 个工具已注册)[/muted]"
            )

    # ──────── 工具发现 ────────

    def get_openai_tools(self) -> list[dict]:
        """
        将所有已注册的 MCP 工具转换为 OpenAI function calling 格式。

        Returns:
            OpenAI tools 格式的工具定义列表。
        """
        tools: list[dict] = []

        for server_name, conn in self._connections.items():
            for tool in conn.tools:
                # 只包含成功注册的工具
                if tool.name not in self._tool_to_server:
                    continue
                if self._tool_to_server[tool.name] != server_name:
                    continue

                # MCP inputSchema → OpenAI parameters
                parameters = (
                    dict(tool.inputSchema)
                    if hasattr(tool, "inputSchema") and tool.inputSchema
                    else {"type": "object", "properties": {}}
                )
                # 移除 $schema 字段（部分 MCP 服务器会带上，OpenAI 不接受）
                parameters.pop("$schema", None)

                tools.append(
                    {
                        "type": "function",
                        "function": {
                            "name": tool.name,
                            "description": (tool.description or f"MCP tool from {server_name}"),
                            "parameters": parameters,
                        },
                    }
                )

        return tools

    # ──────── 工具调用 ────────

    def is_mcp_tool(self, tool_name: str) -> bool:
        """判断工具名是否为已注册的 MCP 工具。"""
        return tool_name in self._tool_to_server

    async def call_tool(self, tool_name: str, arguments: dict) -> str:
        """
        调用指定的 MCP 工具。

        自动路由到正确的 MCP 服务器。

        Args:
            tool_name:  工具名称
            arguments:  工具参数

        Returns:
            工具执行结果文本。
        """
        server_name = self._tool_to_server.get(tool_name)
        if not server_name or server_name not in self._connections:
            return f"MCP 工具 '{tool_name}' 未找到"

        conn = self._connections[server_name]
        try:
            return await conn.call_tool(tool_name, arguments)
        except Exception as e:
            return f"MCP 工具 '{tool_name}' 执行失败: {e}"

    # ──────── 信息展示 ────────

    def get_tool_summary(self) -> str:
        """获取所有已注册 MCP 工具的摘要信息。"""
        parts: list[str] = []
        for server_name, conn in self._connections.items():
            tool_names = [
                t.name
                for t in conn.tools
                if t.name in self._tool_to_server and self._tool_to_server[t.name] == server_name
            ]
            if tool_names:
                parts.append(f"  • {server_name}: {', '.join(tool_names)}")
        return "\n".join(parts)

    @property
    def server_count(self) -> int:
        """已连接的 MCP 服务器数量。"""
        return len(self._connections)

    @property
    def tool_count(self) -> int:
        """已注册的 MCP 工具总数。"""
        return len(self._tool_to_server)

    # ──────── 生命周期 ────────

    async def close(self):
        """关闭所有 MCP 服务器连接。"""
        for conn in self._connections.values():
            await conn.close()
        self._connections.clear()
        self._tool_to_server.clear()
