from pathlib import Path
from typing import Any, Dict, Optional

from fastmcp.client import Client
from fastmcp.client.transports import FastMCPTransport


class SMFInspector:
    """Interactive inspector for SMF servers."""

    def __init__(self, server_path: str):
        """
        Initialize inspector.

        Args:
            server_path: Path to server file (e.g., "server.py:mcp")
        """
        self.server_path = server_path
        self.client: Optional[Client[FastMCPTransport]] = None
        self.server: Optional[Any] = None

    def _load_server(self) -> None:
        """Load server module."""
        if self.server is not None:
            return  # Already loaded

        # Import server module
        if ":" in self.server_path:
            file_path, server_var = self.server_path.split(":", 1)
        else:
            file_path = self.server_path
            server_var = "mcp"

        file_path = Path(file_path)
        if not file_path.exists():
            raise FileNotFoundError(f"Server file not found: {file_path}")

        import importlib.util

        spec = importlib.util.spec_from_file_location("server", file_path)
        if spec is None or spec.loader is None:
            raise ImportError(f"Cannot import server from {file_path}")

        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)

        if not hasattr(module, server_var):
            raise AttributeError(f"Server module must define '{server_var}' variable")

        self.server = getattr(module, server_var)

    async def connect(self) -> None:
        """Connect to the server."""
        self._load_server()
        
        # Create client - FastMCP transport uses the server directly
        self.client = Client(transport=self.server)
        await self.client.__aenter__()

    async def disconnect(self) -> None:
        """Disconnect from the server."""
        if self.client:
            try:
                await self.client.__aexit__(None, None, None)
            except Exception:
                pass  # Ignore errors on disconnect
            finally:
                self.client = None

    async def list_tools(self) -> list:
        """List all available tools."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        return await self.client.list_tools()

    async def list_resources(self) -> list:
        """List all available resources."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        return await self.client.list_resources()

    async def list_prompts(self) -> list:
        """List all available prompts."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        return await self.client.list_prompts()

    async def call_tool(self, name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
        """Call a tool."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        result = await self.client.call_tool(name=name, arguments=arguments)
        # Extract data from result object (CallToolResult)
        # FastMCP Client returns CallToolResult with .data attribute
        response: Dict[str, Any] = {}
        if hasattr(result, "data"):
            response["data"] = result.data
        if hasattr(result, "isError"):
            response["isError"] = result.isError
        if hasattr(result, "content"):
            response["content"] = result.content
        if hasattr(result, "text"):
            response["text"] = result.text
        # If no standard attributes, try to serialize the object
        if not response:
            try:
                if hasattr(result, "__dict__"):
                    response = result.__dict__
                else:
                    response = {"result": str(result)}
            except Exception:
                response = {"result": str(result)}
        return response

    async def read_resource(self, uri: str) -> Dict[str, Any]:
        """Read a resource."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        result = await self.client.read_resource(uri=uri)
        # Extract data from result object (ReadResourceResult)
        response: Dict[str, Any] = {}
        if hasattr(result, "contents"):
            # contents is typically a list
            contents = result.contents
            if isinstance(contents, list):
                response["contents"] = [
                    {
                        "uri": getattr(c, "uri", None),
                        "mimeType": getattr(c, "mimeType", None),
                        "text": getattr(c, "text", None),
                        "blob": getattr(c, "blob", None),
                    }
                    for c in contents
                ]
            else:
                response["contents"] = contents
        if hasattr(result, "data"):
            response["data"] = result.data
        if not response:
            try:
                if hasattr(result, "__dict__"):
                    response = result.__dict__
                else:
                    response = {"result": str(result)}
            except Exception:
                response = {"result": str(result)}
        return response

    async def get_prompt(self, name: str, arguments: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Get a prompt."""
        if not self.client:
            raise RuntimeError("Client not connected. Call connect() first.")
        result = await self.client.get_prompt(name=name, arguments=arguments or {})
        # Extract data from result object (GetPromptResult)
        response: Dict[str, Any] = {}
        if hasattr(result, "messages"):
            # messages is typically a list of message objects
            messages = result.messages
            if isinstance(messages, list):
                response["messages"] = [
                    {
                        "role": getattr(m, "role", None),
                        "content": getattr(m, "content", None),
                        "text": getattr(m, "text", None),
                    }
                    for m in messages
                ]
            else:
                response["messages"] = messages
        if hasattr(result, "content"):
            response["content"] = result.content
        if hasattr(result, "text"):
            response["text"] = result.text
        if not response:
            try:
                if hasattr(result, "__dict__"):
                    response = result.__dict__
                else:
                    response = {"result": str(result)}
            except Exception:
                response = {"result": str(result)}
        return response
