"""
Phase 79: MCP Router & Microservices.

Splits the monolithic server.py into domain-specific MCP microservices
that can run independently on separate ports or be merged into a single
server via the plugin loader.

Architecture:
  - Each domain is a plugin module in mcp_plugins/ exposing TOOLS + HANDLERS
  - This router can launch any domain as a standalone MCP server
  - The monolith (server.py) auto-loads all plugins via mcp_plugin_loader.py

Usage (standalone microservices):
  python -m brynq.mcp_router --domain knowledge --port 8001
  python -m brynq.mcp_router --domain swarm --port 8002
  python -m brynq.mcp_router --domain rag --port 8003
  python -m brynq.mcp_router --list-domains

Usage (merged — default in server.py):
  All plugins are loaded automatically by MCPServerPluginLoader.
"""

import argparse
import asyncio
import importlib
import inspect
import json
import logging
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%H:%M:%S",
)
log = logging.getLogger("brynq.mcp_router")

# ── Domain Registry ────────────────────────────────────────────────────────

PLUGINS_DIR = Path(__file__).parent / "mcp_plugins"

# Map of domain name -> module name in mcp_plugins/
DOMAIN_REGISTRY: Dict[str, str] = {
    "core": "core_system",
    "tasks": "tasks",
    "memory": "memory",
    "governance": "governance",
    "knowledge": "knowledge_tools",
    "messaging": "messaging_tools",
    "swarm": "swarm_tools",
    "rag": "rag_tools",
    "workflow": "workflow",
    "security": "security",
}


def list_domains() -> List[dict]:
    """List all available MCP domains with their tool counts."""
    domains = []
    for domain_name, module_name in DOMAIN_REGISTRY.items():
        try:
            tools, handlers = _load_domain(domain_name)
            domains.append({
                "domain": domain_name,
                "module": module_name,
                "tools": len(tools),
                "handlers": len(handlers),
                "tool_names": [t.name for t in tools],
            })
        except Exception as e:
            domains.append({
                "domain": domain_name,
                "module": module_name,
                "error": str(e),
            })
    return domains


def _load_domain(domain: str) -> Tuple[List[Tool], Dict[str, Any]]:
    """Load tools and handlers for a specific domain."""
    if domain not in DOMAIN_REGISTRY:
        raise ValueError(
            f"Unknown domain '{domain}'. Available: {sorted(DOMAIN_REGISTRY.keys())}"
        )

    module_name = DOMAIN_REGISTRY[domain]
    full_module = f"brynq.mcp_plugins.{module_name}"

    try:
        mod = importlib.import_module(full_module)
    except ImportError as e:
        raise ImportError(f"Failed to import {full_module}: {e}") from e

    tools = getattr(mod, "TOOLS", [])
    handlers = getattr(mod, "HANDLERS", {})

    if not isinstance(tools, list):
        raise TypeError(f"{full_module}.TOOLS must be a list")
    if not isinstance(handlers, dict):
        raise TypeError(f"{full_module}.HANDLERS must be a dict")

    return tools, handlers


def load_all_domains() -> Tuple[List[Tool], Dict[str, Any]]:
    """Load all domains. Used by server.py for the merged monolith."""
    all_tools: List[Tool] = []
    all_handlers: Dict[str, Any] = {}

    for domain in DOMAIN_REGISTRY:
        try:
            tools, handlers = _load_domain(domain)
            all_tools.extend(tools)
            all_handlers.update(handlers)
            log.info("Loaded domain '%s': %d tools", domain, len(tools))
        except Exception as e:
            log.error("Failed to load domain '%s': %s", domain, e)

    return all_tools, all_handlers


# ── Standalone MCP Server ──────────────────────────────────────────────────


def create_domain_server(domain: str) -> Server:
    """Create a standalone MCP server for a single domain."""
    tools, handlers = _load_domain(domain)

    server = Server(f"brynq-{domain}")

    @server.list_tools()
    async def _list_tools() -> list[Tool]:
        return tools

    @server.call_tool()
    async def _call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
        handler = handlers.get(name)
        if not handler:
            return [TextContent(type="text", text=f"Unknown tool: {name}")]

        result = handler(arguments)
        if inspect.isawaitable(result):
            result = await result
        return result

    return server


async def run_domain_server(domain: str) -> None:
    """Run a standalone domain MCP server over stdio."""
    server = create_domain_server(domain)
    log.info("Starting standalone MCP server for domain: %s", domain)

    async with stdio_server() as (read, write):
        await server.run(read, write, server.create_initialization_options())


# ── CLI Entry Point ────────────────────────────────────────────────────────


def main() -> None:
    parser = argparse.ArgumentParser(
        prog="brynq.mcp_router",
        description="Run domain-specific MCP microservices or list available domains.",
    )
    parser.add_argument(
        "--domain",
        choices=list(DOMAIN_REGISTRY.keys()),
        help="Domain to run as a standalone MCP server",
    )
    parser.add_argument(
        "--list-domains",
        action="store_true",
        help="List all available domains and their tools",
    )

    args = parser.parse_args()

    if args.list_domains:
        domains = list_domains()
        print(json.dumps(domains, indent=2))
        return

    if not args.domain:
        parser.error("Specify --domain or --list-domains")

    asyncio.run(run_domain_server(args.domain))


if __name__ == "__main__":
    main()
