"""工具适配器

提供不同工具格式之间的转换：
- BaseTool <-> LangChain Tool
- BaseTool <-> Function Call
- BaseTool <-> MCP Tool
"""

import logging
from typing import Any, Dict, List, Optional

from .base import BaseTool, FunctionCallDefinition, ToolCollection, ToolOutput

logger = logging.getLogger("agent")


# ============================================================================
# LangChain 适配器
# ============================================================================


class LangChainAdapter:
    """LangChain 工具适配器"""

    @staticmethod
    def to_base_tool(langchain_tool) -> BaseTool:
        """将 LangChain Tool 转换为 BaseTool

        Args:
            langchain_tool: LangChain @tool 装饰器函数

        Returns:
            BaseTool 实例
        """
        from .base import BaseTool

        class WrappedTool(BaseTool):
            name = langchain_tool.name
            description = langchain_tool.description
            category = "general"
            input_schema = {"type": "object", "properties": {}}

            def execute(self, parameters: Dict[str, Any], context: Optional[Dict] = None) -> ToolOutput:
                try:
                    result = langchain_tool.invoke(parameters)
                    return ToolOutput(success=True, result=result)
                except Exception as e:
                    return ToolOutput(success=False, result=None, error=str(e))

        return WrappedTool()

    @staticmethod
    def to_langchain(tools: List[BaseTool]) -> List:
        """将 BaseTool 列表转换为 LangChain 工具列表"""
        return [t.to_langchain_tool() for t in tools]

    @staticmethod
    def wrap_async_to_sync(async_tool):
        """将异步 MCP 工具包装为同步工具"""
        from langchain_core.tools import Tool

        name = async_tool.name
        description = async_tool.description

        def sync_func(tool_input) -> str:
            """同步调用入口"""
            try:
                import asyncio
                import json

                # 处理不同格式的输入
                if isinstance(tool_input, dict):
                    if "__arg1" in tool_input:
                        inner = tool_input["__arg1"]
                        if isinstance(inner, dict):
                            params = inner
                        elif isinstance(inner, str):
                            try:
                                params = json.loads(inner)
                            except json.JSONDecodeError:
                                params = {"input": inner}
                        else:
                            params = {"input": str(inner)}
                    else:
                        params = tool_input
                elif isinstance(tool_input, str):
                    try:
                        params = json.loads(tool_input)
                    except json.JSONDecodeError:
                        params = {"input": tool_input}
                else:
                    params = {"input": str(tool_input)}

                result = asyncio.run(async_tool.ainvoke(params))
                return result
            except Exception as e:
                logger.error(f"[LangChain Adapter] {name} 错误: {e}")
                return f"工具调用错误: {str(e)}"

        return Tool(
            name=name,
            func=sync_func,
            description=description,
        )


# ============================================================================
# Function Call 适配器
# ============================================================================


class FunctionCallAdapter:
    """Function Call 适配器"""

    @staticmethod
    def to_base_tool(func_def: FunctionCallDefinition, executor_func) -> BaseTool:
        """将 Function Call 定义转换为 BaseTool"""
        from .base import BaseTool

        class WrappedTool(BaseTool):
            name = func_def.name
            description = func_def.description
            input_schema = func_def.parameters
            category = "general"
            _executor = executor_func

            def execute(self, parameters: Dict[str, Any], context: Optional[Dict] = None) -> ToolOutput:
                try:
                    result = self._executor(parameters, context)
                    return ToolOutput(success=True, result=result)
                except Exception as e:
                    return ToolOutput(success=False, result=None, error=str(e))

        return WrappedTool()

    @staticmethod
    def to_function_call(tools: List[BaseTool]) -> List[FunctionCallDefinition]:
        """将 BaseTool 列表转换为 Function Call 定义列表"""
        return [t.to_function_call() for t in tools]

    @staticmethod
    def to_openai_format(tools: List[BaseTool]) -> List[Dict[str, Any]]:
        """转换为 OpenAI Function Call 格式"""
        return [t.to_function_call().to_openai_format() for t in tools]


# ============================================================================
# MCP 适配器
# ============================================================================


class MCPAdapter:
    """MCP 工具适配器"""

    @staticmethod
    def to_base_tool(mcp_tool) -> BaseTool:
        """将 MCP Tool 转换为 BaseTool"""
        from .base import BaseTool

        class MCPTool(BaseTool):
            name = mcp_tool.name
            description = mcp_tool.description
            category = "mcp"
            input_schema = getattr(mcp_tool, 'args_schema', {}) or {}
            _mcp_tool = mcp_tool

            def execute(self, parameters: Dict[str, Any], context: Optional[Dict] = None) -> ToolOutput:
                try:
                    import asyncio
                    result = asyncio.run(self._mcp_tool.ainvoke(parameters))
                    return ToolOutput(success=True, result=result)
                except Exception as e:
                    return ToolOutput(success=False, result=None, error=str(e))

        return MCPTool()


# MCP 警告标志（确保只显示一次）
_mcp_warning_shown = False


class MCPAdapter:
    """MCP 适配器"""

    @staticmethod
    async def load_mcp_tools(mcp_servers: Dict[str, Any]) -> List[BaseTool]:
        """从 MCP 配置异步加载工具"""
        tools = []

        try:
            from langchain_mcp_adapters.sessions import SSEConnection, StdioConnection
            from langchain_mcp_adapters.tools import load_mcp_tools as langchain_load_mcp

            for server_name, server_config in mcp_servers.items():
                transport = server_config.get("transport", "stdio")
                logger.info(f"[MCP Adapter] 加载服务器: {server_name}, transport: {transport}")

                try:
                    if transport == "stdio":
                        connection = StdioConnection(
                            command=server_config.get("command", ""),
                            args=server_config.get("args", []),
                            cwd=server_config.get("cwd", None),
                            env=server_config.get("env", None),
                            transport=transport,
                        )
                    elif transport == "http":
                        connection = SSEConnection(
                            url=server_config.get("url", ""),
                            transport=transport,
                        )
                    else:
                        logger.warning(f"[MCP Adapter] 未知的 transport: {transport}")
                        continue

                    server_tools = await langchain_load_mcp(
                        session=None,
                        connection=connection,
                        server_name=server_name,
                        tool_name_prefix=True,
                    )

                    # 转换为 BaseTool
                    for t in server_tools:
                        base_tool = MCPAdapter.to_base_tool(t)
                        # 包装异步工具为同步
                        base_tool._mcp_tool = LangChainAdapter.wrap_async_to_sync(t)
                        tools.append(base_tool)
                        logger.info(f"[MCP Adapter] 加载工具: {t.name}")

                except Exception as e:
                    logger.error(f"[MCP Adapter] 加载 {server_name} 失败: {e}")

        except ImportError as e:
            global _mcp_warning_shown
            if not _mcp_warning_shown:
                logger.warning(f"[MCP Adapter] langchain-mcp-adapters 未安装: {e}")
                _mcp_warning_shown = True

        return tools


# ============================================================================
# 统一适配器
# ============================================================================


class ToolAdapter:
    """统一工具适配器 - 提供一站式格式转换"""

    @staticmethod
    def create_collection(
        builtin_tools: Optional[List[BaseTool]] = None,
        mcp_servers: Optional[Dict[str, Any]] = None,
    ) -> ToolCollection:
        """创建工具集合

        Args:
            builtin_tools: 内置工具列表
            mcp_servers: MCP 服务器配置

        Returns:
            ToolCollection: 工具集合
        """
        collection = ToolCollection()

        # 添加内置工具
        if builtin_tools:
            for tool in builtin_tools:
                collection.add(tool)

        # 加载 MCP 工具
        if mcp_servers:
            try:
                import asyncio
                mcp_tools = asyncio.run(MCPAdapter.load_mcp_tools(mcp_servers))
                for tool in mcp_tools:
                    collection.add(tool)
            except Exception as e:
                logger.warning(f"[ToolAdapter] MCP 工具加载失败: {e}")

        return collection
