# coding=utf-8
"""
MCPTool 执行器和转换器。

MCP 工具执行逻辑：
1. 检查运行模式（仅支持 standalone 模式）
2. 连接到 MCP 服务器（stdio/http/ws）
3. 发现服务器上的工具列表
4. 将 MCP 工具转换为标准 Tool 格式
5. 调用指定的 MCP 资源

使用方式：
- 使用 MCPToolConverter 将 MCPTool 列表转换为标准 Tool 列表
- 在 convert_tools_to_openai_functions 中支持 MCP 工具
- 使用 MCPToolRuntime 执行单个 MCP 工具调用
"""

from __future__ import annotations

import os
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Union
import time
import uuid

from loguru import logger

from turbo_agent_core.schema.enums import JSON, RunType
from turbo_agent_core.schema.events import (
    BaseEvent,
    RunLifecycleCreatedEvent,
    RunLifecycleCreatedPayload,
    RunLifecycleCompletedEvent,
    RunLifecycleCompletedPayload,
    RunLifecycleFailedEvent,
    RunLifecycleFailedPayload,
    ContentAgentResultStartEvent,
    ContentAgentResultStartPayload,
    ContentAgentResultEndEvent,
    ContentAgentResultEndPayload,
    ExecutorMetadata,
    UserInfo,
)
from turbo_agent_core.schema.agents import MCPTool, Tool
from turbo_agent_core.schema.external import (
    McpServerSpec,
    McpResourceDescriptor,
    McpTransport,
    Platform,
    Secret,
)
from turbo_agent_core.schema.basic import Parameter
from turbo_agent_runtime.utils.executor_identity import build_executor_id


# 环境变量配置
ENV_TURBO_AGENT_MODE = "TURBO_AGENT_MODE"
MODE_STANDALONE = "standalone"


def is_standalone_mode() -> bool:
    """检查是否处于 standalone 运行模式。"""
    return os.getenv(ENV_TURBO_AGENT_MODE, "").lower() == MODE_STANDALONE


class MCPModeError(Exception):
    """MCP 运行模式错误。"""
    pass


class MCPToolConverter:
    """MCP 工具转换器。
    
    将 MCPTool 转换为标准 Tool 格式，用于：
    - 在 convert_tools_to_openai_functions 中支持 MCP 工具
    - 将 MCP 服务器的工具列表展平为标准 Tool 列表
    
    转换规则：
    - MCPTool.server 定义了 MCP 服务器连接配置
    - MCPTool.resource 定义了具体的 MCP 资源/工具
    - 转换后的 Tool 保留 MCP 相关信息，便于运行时识别和调用
    """

    @staticmethod
    def convert_to_standard_tool(mcp_tool: MCPTool) -> Tool:
        """将单个 MCPTool 转换为标准 Tool。
        
        Args:
            mcp_tool: MCP 工具定义
            
        Returns:
            标准 Tool 对象
        """
        # 构建工具 ID 和名称
        tool_id = mcp_tool.id
        tool_name = mcp_tool.name
        
        # 使用 MCP resource 的 schema 作为工具的 schema
        input_schema = mcp_tool.resource.input_schema or {}
        output_schema = mcp_tool.resource.output_schema or {}
        
        # 构建描述
        description = mcp_tool.description or mcp_tool.resource.description or ""
        if mcp_tool.server:
            description = f"[{mcp_tool.server.name}] {description}"
        
        # 创建标准 Tool
        # 注意：这里创建一个特殊的 Tool，其 run_type 会被设置为 MCP
        # 实际执行时需要通过 runtime 识别并路由到 MCPToolRuntime
        standard_tool = Tool(
            id=tool_id,
            name=tool_name,
            space_name=getattr(mcp_tool, "space_name", None) or "",
            project_name_id=getattr(mcp_tool, "project_name_id", None) or "",
            belong_to_project_path=mcp_tool.belong_to_project_path,
            name_id=mcp_tool.name_id,
            description=description,
            input_schema=input_schema,
            output_schema=output_schema,
            input=[],  # 从 schema 推导
            output=[],  # 从 schema 推导
        )
        
        # 标记为 MCP 工具（通过 extra 字段）
        if not standard_tool.input_schema:
            standard_tool.input_schema = {}
        if isinstance(standard_tool.input_schema, dict):
            standard_tool.input_schema["_mcp_tool_id"] = tool_id
            standard_tool.input_schema["_mcp_server_id"] = mcp_tool.server.id if mcp_tool.server else None
            standard_tool.input_schema["_mcp_resource_id"] = mcp_tool.resource.id
        
        return standard_tool

    @staticmethod
    def convert_tools_to_openai_functions(
        tools: List[Union[Tool, MCPTool]],
        parameters_to_json_schema: Optional[Any] = None,
    ) -> List[Dict[str, Any]]:
        """将工具列表（包含 MCP 工具）转换为 OpenAI function 格式。
        
        这是 convert_tools_to_openai_functions 的 MCP 感知版本。
        
        Args:
            tools: 工具列表（可包含 Tool 和 MCPTool）
            parameters_to_json_schema: 参数到 JSON schema 的转换函数
            
        Returns:
            OpenAI function 格式列表
        """
        converted: List[Dict[str, Any]] = []
        
        for tool in tools or []:
            if isinstance(tool, MCPTool):
                # MCP 工具特殊处理
                schema = MCPToolConverter._extract_input_schema(tool)
                
                # 构建描述
                description = tool.description or ""
                if tool.resource and tool.resource.description:
                    description = tool.resource.description
                if tool.server:
                    description = f"[{tool.server.name}] {description}"
                
                # 构建 function name：保留原始命名
                from turbo_agent_core.utils.identity import build_tool_call_name
                function_name = build_tool_call_name(tool)
                
                converted.append({
                    "type": "function",
                    "function": {
                        "name": function_name,
                        "description": description,
                        "parameters": schema or {},
                    },
                })
            else:
                # 标准工具处理（沿用原有逻辑）
                schema = getattr(tool, "input_schema", None)
                if (schema is None or schema == {}) and parameters_to_json_schema is not None:
                    tool_input = getattr(tool, "input", None)
                    if tool_input:
                        schema = parameters_to_json_schema(tool_input)
                
                from turbo_agent_core.utils.identity import build_tool_call_name
                converted.append({
                    "type": "function",
                    "function": {
                        "name": build_tool_call_name(tool),
                        "description": getattr(tool, "description", None) or "",
                        "parameters": schema or {},
                    },
                })
        
        return converted

    @staticmethod
    def _extract_input_schema(mcp_tool: MCPTool) -> Dict[str, Any]:
        """从 MCPTool 提取输入 schema。
        
        Args:
            mcp_tool: MCP 工具
            
        Returns:
            JSON schema dict
        """
        if mcp_tool.resource and mcp_tool.resource.input_schema:
            return mcp_tool.resource.input_schema
        
        # 从 input 参数推导
        if mcp_tool.input:
            from turbo_agent_core.utils.param_schema import parameters_to_json_schema
            return parameters_to_json_schema(mcp_tool.input)
        
        return {"type": "object", "properties": {}}

    @staticmethod
    async def discover_tools_from_server(
        server: McpServerSpec,
        timeout_ms: Optional[int] = None,
    ) -> List[McpResourceDescriptor]:
        """从 MCP 服务器发现工具列表。
        
        注意：这是一个占位实现，实际的 MCP 发现逻辑需要：
        - 通过 stdio/http/ws 连接到 MCP 服务器
        - 发送 tools/list 请求
        - 解析返回的工具列表
        
        Args:
            server: MCP 服务器配置
            timeout_ms: 超时时间（毫秒）
            
        Returns:
            资源描述列表
            
        Raises:
            MCPModeError: 如果不是 standalone 模式
        """
        if not is_standalone_mode():
            raise MCPModeError(
                f"MCP tool discovery only available in '{MODE_STANDALONE}' mode. "
                f"Current mode: {os.getenv(ENV_TURBO_AGENT_MODE, 'not set')}"
            )
        
        # TODO: 实现实际的 MCP 协议通信
        # 1. 根据 server.transport 选择连接方式
        # 2. 建立连接并发送 tools/list 请求
        # 3. 解析响应并转换为 McpResourceDescriptor 列表
        
        logger.warning("MCP discover_tools_from_server is a placeholder. Implement actual MCP protocol.")
        return []


class MCPToolRuntime(MCPTool):
    """MCP 工具运行时：执行 MCP 协议调用。
    
    执行特点：
    - 仅支持 standalone 模式（通过环境变量检查）
    - 内部通过 MCP 协议与外部服务器通信
    - 外部流式接口封装完整的生命周期事件
    
    限制：
    - 需要 TURBO_AGENT_MODE=standalone 环境变量
    - 暂不支持 stdio 传输方式（需要使用独立进程管理）
    """

    def _resolve_user_metadata(self, **kwargs) -> UserInfo:
        """解析用户信息。"""
        raw = kwargs.get("user_metadata") or kwargs.get("user_info")
        if isinstance(raw, UserInfo):
            return raw
        if isinstance(raw, dict):
            try:
                return UserInfo.model_validate(raw)
            except Exception:
                pass
        user_id = kwargs.get("user_id")
        username = kwargs.get("username")
        return UserInfo(id=str(user_id or "local"), username=str(username or "local"))

    def _check_mode(self) -> None:
        """检查运行模式。"""
        if not is_standalone_mode():
            raise MCPModeError(
                f"MCPTool only available in '{MODE_STANDALONE}' mode. "
                f"Current mode: {os.getenv(ENV_TURBO_AGENT_MODE, 'not set')}. "
                f"Please set {ENV_TURBO_AGENT_MODE}={MODE_STANDALONE}"
            )

    async def _call_mcp_resource(
        self,
        input_data: Dict[str, Any],
        timeout_ms: Optional[int] = None,
    ) -> Dict[str, Any]:
        """调用 MCP 资源。
        
        Args:
            input_data: 输入参数
            timeout_ms: 超时时间（覆盖工具配置）
            
        Returns:
            调用结果
            
        Raises:
            MCPModeError: 如果不是 standalone 模式
            NotImplementedError: 实际的 MCP 协议尚未实现
        """
        self._check_mode()
        
        # 获取超时配置
        timeout = timeout_ms or self.timeout_ms or 30000
        
        # TODO: 实现实际的 MCP 协议调用
        # 1. 根据 self.server.transport 选择连接方式
        # 2. 建立连接
        # 3. 发送调用请求
        # 4. 等待响应并返回
        
        server_info = f"{self.server.name} ({self.server.transport.value})" if self.server else "unknown"
        logger.debug(f"MCP call to {server_info}, resource={self.resource.id}, timeout={timeout}ms")
        
        raise NotImplementedError(
            "MCP protocol implementation is a placeholder. "
            "Please implement actual MCP client logic."
        )

    def run(self, input: JSON, **kwargs) -> JSON:
        """同步执行 MCP 工具。
        
        MCP 工具本质上需要异步通信，同步方法返回错误。
        """
        try:
            self._check_mode()
        except MCPModeError as e:
            return {"error": str(e), "tool_id": self.id}
        
        return {
            "error": "MCPTool does not support synchronous execution",
            "tool_id": self.id,
            "message": "Please use a_run or a_stream instead",
        }

    async def a_run(self, input: JSON, **kwargs) -> JSON:
        """异步执行 MCP 工具。
        
        流程：
        1. 检查 standalone 模式
        2. 验证输入
        3. 建立 MCP 连接
        4. 调用 MCP 资源
        5. 验证输出并返回
        """
        try:
            # 检查模式
            self._check_mode()
            
            # 验证输入
            data = input if isinstance(input, dict) else {"value": input}
            valid_input = self.validate_input(data)
            
            # 调用 MCP 资源
            result = await self._call_mcp_resource(valid_input)
            
            # 验证输出
            shaped = self.validate_output(result)
            
            return {
                "tool_id": self.id,
                "version_id": getattr(self, "version_id", None),
                "output": shaped,
                "raw": result,
            }
            
        except MCPModeError as e:
            logger.error(f"MCP mode error: {e}")
            return {"error": str(e), "tool_id": self.id, "status": "mode_error"}
        except NotImplementedError as e:
            logger.warning(f"MCP not implemented: {e}")
            return {"error": str(e), "tool_id": self.id, "status": "not_implemented"}
        except Exception as e:
            logger.exception(f"MCPTool 异步执行失败: {e}")
            return {"error": str(e), "tool_id": self.id, "status": "error"}

    def stream(self, input: JSON, **kwargs) -> Iterator[BaseEvent]:
        """同步流式执行（MCP 不支持真正的同步流式）。
        
        返回单一错误事件。
        """
        trace_id = str(uuid.uuid4())
        run_id = f"run_{self.id}_{int(time.time() * 1000)}"
        executor_type = self.run_type
        executor_id = build_executor_id(self, executor_type)
        executor_path = [executor_id]

        error_msg = "MCPTool does not support synchronous stream"
        if not is_standalone_mode():
            error_msg = f"MCPTool only available in '{MODE_STANDALONE}' mode"

        yield RunLifecycleFailedEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_id=executor_id,
            executor_type=executor_type,
            executor_path=executor_path,
            payload=RunLifecycleFailedPayload(
                error={"code": "NotSupported", "message": error_msg}
            ),
        )

    async def a_stream(self, input: JSON, **kwargs) -> AsyncIterator[BaseEvent]:
        """异步流式执行 MCP 工具。
        
        流程：
        1. 发送生命周期开始事件
        2. 检查 standalone 模式
        3. 调用 MCP 资源
        4. 发送结果事件
        5. 发送生命周期结束事件
        """
        trace_id = str(uuid.uuid4())
        run_id = f"run_{self.id}_{int(time.time() * 1000)}"
        executor_type = self.run_type
        executor_id = build_executor_id(self, executor_type)
        executor_path = [executor_id]

        # 发送生命周期开始事件
        yield RunLifecycleCreatedEvent(
            trace_id=trace_id,
            trace_path=[],
            run_id=run_id,
            run_path=[run_id],
            executor_id=executor_id,
            executor_type=executor_type,
            executor_path=executor_path,
            executor_metadata=ExecutorMetadata(
                id=self.id,
                name=self.name,
                belong_to_project_path=self.belong_to_project_path,
                name_id=self.name_id,
                description=self.description,
                avatar_uri=self.avatar_uri,
                run_type=self.run_type,
                version_id=self.version_id,
                version=self.version_tag,
            ),
            user_metadata=self._resolve_user_metadata(**kwargs),
            payload=RunLifecycleCreatedPayload(input_data=input),
        )

        try:
            # 检查模式
            self._check_mode()
            
            # 发送结果开始事件
            yield ContentAgentResultStartEvent(
                trace_id=trace_id,
                run_id=run_id,
                executor_id=executor_id,
                executor_type=executor_type,
                executor_path=executor_path,
                payload=ContentAgentResultStartPayload(mode="json"),
            )

            # 验证输入
            data = input if isinstance(input, dict) else {"value": input}
            valid_input = self.validate_input(data)
            
            # 调用 MCP 资源
            result = await self._call_mcp_resource(valid_input)
            
            # 验证输出
            shaped = self.validate_output(result)
            
            # 发送结果结束事件
            yield ContentAgentResultEndEvent(
                trace_id=trace_id,
                run_id=run_id,
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=ContentAgentResultEndPayload(
                    status="success",
                    full_result=shaped,
                ),
            )
            
            # 发送生命周期完成事件
            yield RunLifecycleCompletedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=RunLifecycleCompletedPayload(
                    output=shaped,
                    usage={"total_tokens": 0},
                ),
            )

        except MCPModeError as e:
            logger.error(f"MCP mode error: {e}")
            yield ContentAgentResultEndEvent(
                trace_id=trace_id,
                run_id=run_id,
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=ContentAgentResultEndPayload(
                    status="error",
                    full_result={"error": str(e)},
                ),
            )
            yield RunLifecycleFailedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=RunLifecycleFailedPayload(
                    error={"code": "MCPModeError", "message": str(e)}
                ),
            )
            
        except NotImplementedError as e:
            logger.warning(f"MCP not implemented: {e}")
            yield ContentAgentResultEndEvent(
                trace_id=trace_id,
                run_id=run_id,
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=ContentAgentResultEndPayload(
                    status="error",
                    full_result={"error": str(e), "note": "MCP protocol not yet implemented"},
                ),
            )
            yield RunLifecycleFailedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=RunLifecycleFailedPayload(
                    error={"code": "NotImplemented", "message": str(e)}
                ),
            )
            
        except Exception as e:
            logger.exception(f"MCPTool 异步流式执行失败: {e}")
            yield ContentAgentResultEndEvent(
                trace_id=trace_id,
                run_id=run_id,
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=ContentAgentResultEndPayload(
                    status="error",
                    full_result=str(e),
                ),
            )
            yield RunLifecycleFailedEvent(
                trace_id=trace_id,
                trace_path=[],
                run_id=run_id,
                run_path=[run_id],
                executor_id=executor_id,
                executor_path=executor_path,
                executor_type=executor_type,
                payload=RunLifecycleFailedPayload(
                    error={"code": "RuntimeError", "message": str(e)}
                ),
            )


# 便捷函数
def check_mcp_available() -> bool:
    """检查 MCP 工具是否可用（standalone 模式）。
    
    Returns:
        是否可用
    """
    return is_standalone_mode()


def require_standalone_mode(func):
    """装饰器：要求 standalone 模式。"""
    def wrapper(*args, **kwargs):
        if not is_standalone_mode():
            raise MCPModeError(
                f"Function '{func.__name__}' only available in '{MODE_STANDALONE}' mode"
            )
        return func(*args, **kwargs)
    return wrapper
