"""Utilities for inspecting FastMCP instances."""

from __future__ import annotations

import importlib.metadata
from dataclasses import dataclass
from typing import Any

from mcp.server.fastmcp import FastMCP as FastMCP1x

import fastmcp
from fastmcp.server.server import FastMCP


@dataclass
class ToolInfo:
    """Information about a tool."""

    key: str
    name: str
    description: str | None
    input_schema: dict[str, Any]
    annotations: dict[str, Any] | None = None
    tags: list[str] | None = None
    enabled: bool | None = None


@dataclass
class PromptInfo:
    """Information about a prompt."""

    key: str
    name: str
    description: str | None
    arguments: list[dict[str, Any]] | None = None
    tags: list[str] | None = None
    enabled: bool | None = None


@dataclass
class ResourceInfo:
    """Information about a resource."""

    key: str
    uri: str
    name: str | None
    description: str | None
    mime_type: str | None = None
    tags: list[str] | None = None
    enabled: bool | None = None


@dataclass
class TemplateInfo:
    """Information about a resource template."""

    key: str
    uri_template: str
    name: str | None
    description: str | None
    mime_type: str | None = None
    tags: list[str] | None = None
    enabled: bool | None = None


@dataclass
class FastMCPInfo:
    """Information extracted from a FastMCP instance."""

    name: str
    instructions: str | None
    fastmcp_version: str
    mcp_version: str
    server_version: str
    tools: list[ToolInfo]
    prompts: list[PromptInfo]
    resources: list[ResourceInfo]
    templates: list[TemplateInfo]
    capabilities: dict[str, Any]


async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
    """Extract information from a FastMCP v2.x instance.

    Args:
        mcp: The FastMCP v2.x instance to inspect

    Returns:
        FastMCPInfo dataclass containing the extracted information
    """
    # Get all the components using FastMCP2's direct methods
    tools_dict = await mcp.get_tools()
    prompts_dict = await mcp.get_prompts()
    resources_dict = await mcp.get_resources()
    templates_dict = await mcp.get_resource_templates()

    # Extract detailed tool information
    tool_infos = []
    for key, tool in tools_dict.items():
        # Convert to MCP tool to get input schema
        mcp_tool = tool.to_mcp_tool(name=key)
        tool_infos.append(
            ToolInfo(
                key=key,
                name=tool.name or key,
                description=tool.description,
                input_schema=mcp_tool.inputSchema if mcp_tool.inputSchema else {},
                annotations=tool.annotations.model_dump() if tool.annotations else None,
                tags=list(tool.tags) if tool.tags else None,
                enabled=tool.enabled,
            )
        )

    # Extract detailed prompt information
    prompt_infos = []
    for key, prompt in prompts_dict.items():
        prompt_infos.append(
            PromptInfo(
                key=key,
                name=prompt.name or key,
                description=prompt.description,
                arguments=[arg.model_dump() for arg in prompt.arguments]
                if prompt.arguments
                else None,
                tags=list(prompt.tags) if prompt.tags else None,
                enabled=prompt.enabled,
            )
        )

    # Extract detailed resource information
    resource_infos = []
    for key, resource in resources_dict.items():
        resource_infos.append(
            ResourceInfo(
                key=key,
                uri=key,  # For v2, key is the URI
                name=resource.name,
                description=resource.description,
                mime_type=resource.mime_type,
                tags=list(resource.tags) if resource.tags else None,
                enabled=resource.enabled,
            )
        )

    # Extract detailed template information
    template_infos = []
    for key, template in templates_dict.items():
        template_infos.append(
            TemplateInfo(
                key=key,
                uri_template=key,  # For v2, key is the URI template
                name=template.name,
                description=template.description,
                mime_type=template.mime_type,
                tags=list(template.tags) if template.tags else None,
                enabled=template.enabled,
            )
        )

    # Basic MCP capabilities that FastMCP supports
    capabilities = {
        "tools": {"listChanged": True},
        "resources": {"subscribe": False, "listChanged": False},
        "prompts": {"listChanged": False},
        "logging": {},
    }

    return FastMCPInfo(
        name=mcp.name,
        instructions=mcp.instructions,
        fastmcp_version=fastmcp.__version__,
        mcp_version=importlib.metadata.version("mcp"),
        server_version=fastmcp.__version__,  # v2.x uses FastMCP version
        tools=tool_infos,
        prompts=prompt_infos,
        resources=resource_infos,
        templates=template_infos,
        capabilities=capabilities,
    )


async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
    """Extract information from a FastMCP v1.x instance using a Client.

    Args:
        mcp: The FastMCP v1.x instance to inspect

    Returns:
        FastMCPInfo dataclass containing the extracted information
    """
    from fastmcp import Client

    # Use a client to interact with the FastMCP1x server
    async with Client(mcp) as client:
        # Get components via client calls (these return MCP objects)
        mcp_tools = await client.list_tools()
        mcp_prompts = await client.list_prompts()
        mcp_resources = await client.list_resources()

        # Try to get resource templates (FastMCP 1.x does have templates)
        try:
            mcp_templates = await client.list_resource_templates()
        except Exception:
            mcp_templates = []

        # Extract detailed tool information from MCP Tool objects
        tool_infos = []
        for mcp_tool in mcp_tools:
            # Extract annotations if they exist
            annotations = None
            if hasattr(mcp_tool, "annotations") and mcp_tool.annotations:
                if hasattr(mcp_tool.annotations, "model_dump"):
                    annotations = mcp_tool.annotations.model_dump()
                elif isinstance(mcp_tool.annotations, dict):
                    annotations = mcp_tool.annotations
                else:
                    annotations = None

            tool_infos.append(
                ToolInfo(
                    key=mcp_tool.name,  # For 1.x, key and name are the same
                    name=mcp_tool.name,
                    description=mcp_tool.description,
                    input_schema=mcp_tool.inputSchema if mcp_tool.inputSchema else {},
                    annotations=annotations,
                    tags=None,  # 1.x doesn't have tags
                    enabled=None,  # 1.x doesn't have enabled field
                )
            )

        # Extract detailed prompt information from MCP Prompt objects
        prompt_infos = []
        for mcp_prompt in mcp_prompts:
            # Convert arguments if they exist
            arguments = None
            if hasattr(mcp_prompt, "arguments") and mcp_prompt.arguments:
                arguments = [arg.model_dump() for arg in mcp_prompt.arguments]

            prompt_infos.append(
                PromptInfo(
                    key=mcp_prompt.name,  # For 1.x, key and name are the same
                    name=mcp_prompt.name,
                    description=mcp_prompt.description,
                    arguments=arguments,
                    tags=None,  # 1.x doesn't have tags
                    enabled=None,  # 1.x doesn't have enabled field
                )
            )

        # Extract detailed resource information from MCP Resource objects
        resource_infos = []
        for mcp_resource in mcp_resources:
            resource_infos.append(
                ResourceInfo(
                    key=str(mcp_resource.uri),  # For 1.x, key and uri are the same
                    uri=str(mcp_resource.uri),
                    name=mcp_resource.name,
                    description=mcp_resource.description,
                    mime_type=mcp_resource.mimeType,
                    tags=None,  # 1.x doesn't have tags
                    enabled=None,  # 1.x doesn't have enabled field
                )
            )

        # Extract detailed template information from MCP ResourceTemplate objects
        template_infos = []
        for mcp_template in mcp_templates:
            template_infos.append(
                TemplateInfo(
                    key=str(
                        mcp_template.uriTemplate
                    ),  # For 1.x, key and uriTemplate are the same
                    uri_template=str(mcp_template.uriTemplate),
                    name=mcp_template.name,
                    description=mcp_template.description,
                    mime_type=mcp_template.mimeType,
                    tags=None,  # 1.x doesn't have tags
                    enabled=None,  # 1.x doesn't have enabled field
                )
            )

        # Basic MCP capabilities
        capabilities = {
            "tools": {"listChanged": True},
            "resources": {"subscribe": False, "listChanged": False},
            "prompts": {"listChanged": False},
            "logging": {},
        }

        return FastMCPInfo(
            name=mcp.name,
            instructions=getattr(mcp, "instructions", None),
            fastmcp_version=fastmcp.__version__,  # Report current fastmcp version
            mcp_version=importlib.metadata.version("mcp"),
            server_version="1.0",  # FastMCP 1.x version
            tools=tool_infos,
            prompts=prompt_infos,
            resources=resource_infos,
            templates=template_infos,  # FastMCP1x does have templates
            capabilities=capabilities,
        )


def _is_fastmcp_v1(mcp: Any) -> bool:
    """Check if the given instance is a FastMCP v1.x instance."""

    # Check if it's an instance of FastMCP1x and not FastMCP2
    return isinstance(mcp, FastMCP1x) and not isinstance(mcp, FastMCP)


async def inspect_fastmcp(mcp: FastMCP[Any] | Any) -> FastMCPInfo:
    """Extract information from a FastMCP instance into a dataclass.

    This function automatically detects whether the instance is FastMCP v1.x or v2.x
    and uses the appropriate extraction method.

    Args:
        mcp: The FastMCP instance to inspect (v1.x or v2.x)

    Returns:
        FastMCPInfo dataclass containing the extracted information
    """
    if _is_fastmcp_v1(mcp):
        return await inspect_fastmcp_v1(mcp)
    else:
        return await inspect_fastmcp_v2(mcp)
