"""CloudSense Upgrade Compass MCP server.

Run with: python -m cloudsense_upgrade_compass.server
Or via CLI: cloudsense-upgrade-compass
"""

import asyncio
import logging
import sys

from anyio import BrokenResourceError, ClosedResourceError
from mcp.server import Server
from mcp.server.stdio import stdio_server
import mcp.types as types

from . import __version__
from .instructions import SERVER_INSTRUCTIONS
from .tools import (
    connect_lma,
    find_customer,
    get_customer_licenses,
    init_assessment,
    get_installed_packages,
    get_customer_metadata,
    get_managed_package_objects,
    get_customer_json_settings,
    get_customer_object_customizations,
    get_package_repo_mapping,
    clone_package_repos,
)

logger = logging.getLogger(__name__)

server = Server("cloudsense-upgrade-compass")

TOOLS = {
    "connect_lma": connect_lma,
    "find_customer": find_customer,
    "get_customer_licenses": get_customer_licenses,
    "init_assessment": init_assessment,
    "get_installed_packages": get_installed_packages,
    "get_customer_metadata": get_customer_metadata,
    "get_managed_objects": get_managed_package_objects,
    "get_cs_settings": get_customer_json_settings,
    "get_object_customizations": get_customer_object_customizations,
    "get_repo_mapping": get_package_repo_mapping,
    "clone_package_repos": clone_package_repos,
}


@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Return available tools."""
    return [
        types.Tool(
            name=tool_module.TOOL_DEFINITION["name"],
            description=tool_module.TOOL_DEFINITION["description"],
            inputSchema=tool_module.TOOL_DEFINITION["inputSchema"],
        )
        for tool_module in TOOLS.values()
    ]


@server.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[types.TextContent]:
    """Handle tool calls."""
    tool_module = TOOLS.get(name)
    if tool_module is None:
        raise ValueError(f"Unknown tool: {name}")

    result = await tool_module.run(arguments)
    return [types.TextContent(type="text", text=result)]


def _is_disconnect_error(exc: BaseException) -> bool:
    """Return True if *exc* (or any nested sub-exception) is a client disconnect."""
    if isinstance(exc, (BrokenResourceError, ClosedResourceError)):
        return True
    if isinstance(exc, BaseExceptionGroup):
        return all(_is_disconnect_error(e) for e in exc.exceptions)
    return False


async def _run_server():
    """Start the MCP server on stdio transport."""
    logger.info(
        "CloudSense Upgrade Compass v%s starting...", __version__
    )
    try:
        async with stdio_server() as (read_stream, write_stream):
            init_options = server.create_initialization_options()
            init_options.instructions = SERVER_INSTRUCTIONS
            await server.run(read_stream, write_stream, init_options)
    except BaseExceptionGroup as eg:
        if _is_disconnect_error(eg):
            logger.info("Client disconnected — shutting down cleanly.")
        else:
            raise
    except (BrokenResourceError, ClosedResourceError):
        logger.info("Client disconnected — shutting down cleanly.")


def main():
    """Entry point for the CLI command."""
    logging.basicConfig(level=logging.INFO)
    try:
        asyncio.run(_run_server())
    except KeyboardInterrupt:
        pass
    except (BrokenPipeError, EOFError):
        logger.info("Pipe closed — exiting.")
    sys.exit(0)


if __name__ == "__main__":
    main()
