"""MCP server for codebase indexing."""

import argparse
import asyncio
from io import TextIOWrapper
import socket
import sys

import anyio
from loguru import logger
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool
import uvicorn

from acemcp.config import init_config
from acemcp.logging_config import setup_logging
from acemcp.tools import search_context_tool, shutdown_index_manager
from acemcp.web import create_app

app = Server("acemcp")


@app.list_tools()
async def list_tools() -> list[Tool]:
    """List available MCP tools.

    Returns:
        List of available tools

    """
    return [
        Tool(
            name="search_context",
            description="Search for relevant code context based on a query within a specific project. "
            "This tool automatically performs incremental indexing before searching, "
            "ensuring results are always up-to-date. "
            "Returns formatted text snippets from the codebase that are semantically related to your query. "
            "IMPORTANT: Use forward slashes (/) as path separators in project_root_path, even on Windows.",
            inputSchema={
                "type": "object",
                "properties": {
                    "project_root_path": {
                        "type": "string",
                        "description": "Absolute path to the project root directory. Use forward slashes (/) as separators. Example: C:/Users/username/projects/myproject",
                    },
                    "query": {
                        "type": "string",
                        "description": """Provide a clear natural language description of the code behavior, workflow, or issue you want to locate. You may also add optional keywords to improve semantic matching.
Recommended format:
Natural language description + optional keywords
Examples:
“I want to find where the server handles chunk merging in the file upload process. Keywords: upload chunk merge, file service”
“Locate where the system refreshes cached data after user permissions are updated. Keywords: permission update, cache refresh”
“Find the initialization flow of message queue consumers during startup. Keywords: mq consumer init, subscribe”
“Show me how configuration hot-reload is triggered and applied in the code. Keywords: config reload, hot update”""",
                    },
                },
                "required": ["project_root_path", "query"],
            },
        ),
    ]


@app.call_tool()
async def call_tool(name: str, arguments: dict) -> dict:
    """Handle tool calls.

    Args:
        name: Tool name
        arguments: Tool arguments

    Returns:
        Tool execution results

    """
    logger.info(f"Tool called: {name} with arguments: {arguments}")

    if name == "search_context":
        return await search_context_tool(arguments)

    return {"type": "text", "text": f"Unknown tool: {name}"}


async def run_web_server(port: int) -> None:
    """Run the web management server.

    Args:
        port: Port to run the web server on

    """
    web_app = create_app()
    # Configure uvicorn to use loguru through InterceptHandler
    # This prevents uvicorn from polluting stdout (which breaks MCP stdio protocol)
    config_uvicorn = uvicorn.Config(
        web_app,
        host="0.0.0.0",
        port=port,
        log_level="warning",
        access_log=False,  # Disable access log to reduce noise
        log_config=None,  # Disable default logging config to use our interceptor
    )
    server = uvicorn.Server(config_uvicorn)
    await server.serve()


async def main(base_url: str | None = None, token: str | None = None, web_port: int | None = None) -> None:
    """Run the MCP server.

    Args:
        base_url: Override BASE_URL from command line
        token: Override TOKEN from command line
        web_port: Port for web management interface (None to disable)

    """
    web_task: asyncio.Task | None = None
    try:
        config = init_config(base_url=base_url, token=token)
        config.validate()
        logger.info("Starting acemcp MCP server...")
        logger.info(f"Configuration: index_storage_path={config.index_storage_path}, batch_size={config.batch_size}")
        logger.info(f"API: base_url={config.base_url}")

        if web_port:
            logger.info(f"Starting web management interface on port {web_port}")
            web_task = asyncio.create_task(run_web_server(web_port))

        stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8", newline="\n"))
        stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8", newline="\n"))

        async with stdio_server(stdin=stdin, stdout=stdout) as (read_stream, write_stream):
            await app.run(read_stream, write_stream, app.create_initialization_options())

    except Exception:
        logger.exception("Server error")
        raise
    finally:
        if web_task:
            web_task.cancel()
        await shutdown_index_manager()


def is_port_listening(
    port: int,
    host: str = "127.0.0.1",
) -> bool:
    """检测指定主机的端口是否处于监听状态（TCP）
    :param host: 目标主机，默认本地回环地址
    :param port: 目标端口
    :return: 端口监听返回True，否则False.
    """
    try:
        # 创建TCP socket对象
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            # 设置超时时间
            s.settimeout(1)
            # 尝试连接端口
            s.connect((host, port))
        return True
    except (TimeoutError, ConnectionRefusedError, OSError):
        # 超时、连接被拒绝、系统错误（如端口无效）均视为未监听
        return False


def run() -> None:
    """Entry point for the MCP server."""
    parser = argparse.ArgumentParser(description="Acemcp MCP Server for codebase indexing")
    parser.add_argument("--base-url", type=str, help="Override BASE_URL configuration")
    parser.add_argument("--token", type=str, help="Override TOKEN configuration")
    parser.add_argument("--web-port", type=int, help="Enable web management interface on specified port (e.g., 8080)")

    args = parser.parse_args()

    # If web interface is enabled, initialize log broadcaster before setting up logging
    # This ensures the WebSocket handler is preserved
    if args.web_port:
        from acemcp.web.log_handler import get_log_broadcaster

        get_log_broadcaster()  # Initialize the broadcaster

        if is_port_listening(args.web_port):
            # 避免重复启动
            args.web_port = None

    # Setup logging after log broadcaster is initialized
    # Intercept stdlib logging (uvicorn, fastapi) to prevent stdout pollution
    setup_logging(intercept_stdlib=True)

    asyncio.run(main(base_url=args.base_url, token=args.token, web_port=args.web_port))


if __name__ == "__main__":
    run()
