"""Dynamic tool factory for MCP servers.

This module provides functionality to create BaseTool instances
from MCP server tools, enabling dynamic tool registration.
"""

from typing import Any, Optional

from emdash.agent.mcp.client import MCPToolInfo, MCPError
from emdash.agent.mcp.manager import MCPServerManager
from emdash.agent.tools.base import BaseTool, ToolCategory, ToolResult
from emdash.utils.logger import log


class MCPDynamicTool(BaseTool):
    """Dynamically generated tool wrapper for MCP tools.

    This class wraps an MCP tool to provide the BaseTool interface,
    allowing MCP tools to be used seamlessly with the agent framework.

    Example:
        tool = MCPDynamicTool(
            tool_name="read_file",
            tool_info=mcp_tool_info,
            server_name="filesystem",
            manager=mcp_manager,
        )
        result = tool.execute(path="/tmp/test.txt")
    """

    category = ToolCategory.SEARCH  # Default category for MCP tools

    def __init__(
        self,
        tool_name: str,
        tool_info: MCPToolInfo,
        server_name: str,
        manager: MCPServerManager,
        connection=None,
    ):
        """Initialize a dynamic MCP tool.

        Args:
            tool_name: The registered tool name (may include prefix)
            tool_info: MCP tool information including schema
            server_name: Name of the server this tool belongs to
            manager: MCP server manager for calling the tool
            connection: Optional Kuzu connection (not used for MCP tools)
        """
        # Don't call super().__init__() since we don't need the Kuzu connection
        self.connection = connection

        self.name = tool_name
        self._tool_info = tool_info
        self._server_name = server_name
        self._manager = manager

        # Build description from tool info
        self.description = self._build_description()

    def _build_description(self) -> str:
        """Build tool description including server attribution."""
        desc = self._tool_info.description or f"MCP tool from {self._server_name}"
        # Add server attribution for clarity
        if self._server_name not in desc.lower():
            desc = f"[{self._server_name}] {desc}"
        return desc

    def execute(self, **kwargs) -> ToolResult:
        """Execute the MCP tool with given parameters.

        Args:
            **kwargs: Tool-specific parameters

        Returns:
            ToolResult with the result or error
        """
        try:
            response = self._manager.call_tool(self.name, kwargs)

            if response.success:
                # Convert MCP response to ToolResult
                data = {"result": response.result}
                return ToolResult.success_result(
                    data=data,
                    metadata={
                        "server": self._server_name,
                        "tool": self._tool_info.name,
                    },
                )
            else:
                return ToolResult.error_result(
                    error=response.error or "Unknown MCP error",
                    suggestions=[f"Check parameters for {self.name}"],
                )

        except MCPError as e:
            log.error(f"MCP tool error: {e}")
            return ToolResult.error_result(
                error=str(e),
                suggestions=["Check MCP server connection"],
            )
        except Exception as e:
            log.exception(f"Unexpected error calling MCP tool {self.name}")
            return ToolResult.error_result(
                error=f"Tool execution failed: {e}",
            )

    def get_schema(self) -> dict:
        """Return OpenAI function calling schema.

        Converts MCP inputSchema to OpenAI format.

        Returns:
            OpenAI function schema dict
        """
        # MCP uses JSON Schema format, similar to OpenAI
        input_schema = self._tool_info.input_schema or {}

        # Extract properties and required from MCP schema
        properties = input_schema.get("properties", {})
        required = input_schema.get("required", [])

        # Clean up properties for OpenAI format
        cleaned_properties = {}
        for prop_name, prop_def in properties.items():
            cleaned_prop = {
                "type": prop_def.get("type", "string"),
            }
            if "description" in prop_def:
                cleaned_prop["description"] = prop_def["description"]
            if "enum" in prop_def:
                cleaned_prop["enum"] = prop_def["enum"]
            if "default" in prop_def:
                cleaned_prop["default"] = prop_def["default"]
            if "items" in prop_def:
                cleaned_prop["items"] = prop_def["items"]

            cleaned_properties[prop_name] = cleaned_prop

        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": cleaned_properties,
                    "required": required,
                },
            },
        }


def create_tools_from_mcp(
    manager: MCPServerManager,
    connection=None,
) -> list[BaseTool]:
    """Create BaseTool instances from all MCP server tools.

    This function queries all registered MCP servers for their tools
    and creates MCPDynamicTool instances that can be registered with
    the agent toolkit.

    Args:
        manager: MCP server manager with registered servers
        connection: Optional Kuzu connection (passed to tools)

    Returns:
        List of MCPDynamicTool instances

    Example:
        manager = MCPServerManager(config_path=Path(".emdash/mcp.json"))
        tools = create_tools_from_mcp(manager)
        for tool in tools:
            toolkit.register_tool(tool)
    """
    tools = []

    try:
        all_tools = manager.get_all_tools()

        for tool_name, server_name, tool_info in all_tools:
            try:
                dynamic_tool = MCPDynamicTool(
                    tool_name=tool_name,
                    tool_info=tool_info,
                    server_name=server_name,
                    manager=manager,
                    connection=connection,
                )
                tools.append(dynamic_tool)
                log.debug(f"Created dynamic tool: {tool_name} from {server_name}")
            except Exception as e:
                log.warning(f"Failed to create tool {tool_name}: {e}")

        log.info(f"Created {len(tools)} dynamic MCP tools")

    except Exception as e:
        log.error(f"Failed to create MCP tools: {e}")

    return tools


def create_tools_for_server(
    manager: MCPServerManager,
    server_name: str,
    connection=None,
) -> list[BaseTool]:
    """Create BaseTool instances from a specific MCP server.

    Args:
        manager: MCP server manager
        server_name: Name of the server to get tools from
        connection: Optional Kuzu connection

    Returns:
        List of MCPDynamicTool instances from that server
    """
    tools = []

    # Ensure server is started
    try:
        manager.start_server(server_name)
    except MCPError as e:
        log.error(f"Failed to start server {server_name}: {e}")
        return tools

    # Get all tools and filter by server
    all_tools = manager.get_all_tools()

    for tool_name, srv_name, tool_info in all_tools:
        if srv_name == server_name:
            try:
                dynamic_tool = MCPDynamicTool(
                    tool_name=tool_name,
                    tool_info=tool_info,
                    server_name=server_name,
                    manager=manager,
                    connection=connection,
                )
                tools.append(dynamic_tool)
            except Exception as e:
                log.warning(f"Failed to create tool {tool_name}: {e}")

    return tools
