# Description: HTTP transport implementation for MCP server.
# Description: Uses Starlette and Uvicorn for streamable HTTP mode.

from __future__ import annotations

import json
import logging
from datetime import datetime, timezone
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from starlette.applications import Starlette

logger = logging.getLogger(__name__)


def create_asgi_app() -> "Starlette":
    """Create the ASGI application with MCP and health endpoints.

    Returns:
        Starlette application configured with routes and middleware.
    """
    from starlette.applications import Starlette
    from starlette.middleware import Middleware
    from starlette.middleware.cors import CORSMiddleware
    from starlette.requests import Request
    from starlette.responses import JSONResponse, Response
    from starlette.routing import Route

    from lm_mcp.config import get_config

    config = get_config()

    async def health(request: Request) -> Response:
        """Detailed health check endpoint."""
        from lm_mcp.health import run_health_checks

        result = await run_health_checks(include_connectivity=False)
        status_code = 200 if result.status == "healthy" else 503
        return JSONResponse(result.to_dict(), status_code=status_code)

    async def healthz(request: Request) -> Response:
        """Kubernetes liveness probe endpoint."""
        from lm_mcp.health import run_health_checks

        result = await run_health_checks(include_connectivity=False)
        if result.status == "unhealthy":
            return Response(content="unhealthy", status_code=503)
        return Response(content="ok", status_code=200)

    async def readyz(request: Request) -> Response:
        """Kubernetes readiness probe endpoint."""
        from lm_mcp.health import run_health_checks

        include_connectivity = config.health_check_connectivity
        result = await run_health_checks(include_connectivity=include_connectivity)
        if result.status == "unhealthy":
            return Response(content="not ready", status_code=503)
        return Response(content="ready", status_code=200)

    async def mcp_endpoint(request: Request) -> Response:
        """MCP JSON-RPC endpoint for tool calls.

        Delegates tool execution to the shared execute_tool middleware,
        ensuring consistent behavior (filtering, validation, audit logging,
        session recording) across stdio and HTTP transports.
        """
        from lm_mcp.registry import AWX_TOOLS, EDA_TOOLS, TOOLS
        from lm_mcp.server import _awx_client, _eda_client, _filter_tools, execute_tool

        try:
            body = await request.json()
        except json.JSONDecodeError:
            return JSONResponse(
                {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": None},
                status_code=400,
            )

        method = body.get("method")
        params = body.get("params", {})
        req_id = body.get("id")

        try:
            if method == "tools/list":
                all_tools = list(TOOLS)
                if _awx_client is not None:
                    all_tools.extend(AWX_TOOLS)
                if _eda_client is not None:
                    all_tools.extend(EDA_TOOLS)
                filtered = _filter_tools(all_tools, config)
                result = [
                    {"name": t.name, "description": t.description} for t in filtered
                ]
                return JSONResponse({"jsonrpc": "2.0", "result": result, "id": req_id})

            elif method == "tools/call":
                tool_name = params.get("name")
                arguments = params.get("arguments", {})

                if not tool_name:
                    return JSONResponse(
                        {
                            "jsonrpc": "2.0",
                            "error": {"code": -32602, "message": "Missing tool name"},
                            "id": req_id,
                        },
                        status_code=400,
                    )

                result = await execute_tool(tool_name, arguments)

                # Extract text content from result
                if result and len(result) > 0:
                    text = result[0].text
                    try:
                        content = json.loads(text)
                    except json.JSONDecodeError:
                        content = text
                else:
                    content = None

                return JSONResponse({"jsonrpc": "2.0", "result": content, "id": req_id})

            elif method == "resources/list":
                from lm_mcp.resources import RESOURCES

                result = [{"uri": str(r.uri), "name": r.name} for r in RESOURCES]
                return JSONResponse({"jsonrpc": "2.0", "result": result, "id": req_id})

            elif method == "prompts/list":
                from lm_mcp.prompts import PROMPTS

                result = [{"name": p.name, "description": p.description} for p in PROMPTS]
                return JSONResponse({"jsonrpc": "2.0", "result": result, "id": req_id})

            else:
                return JSONResponse(
                    {
                        "jsonrpc": "2.0",
                        "error": {"code": -32601, "message": f"Method not found: {method}"},
                        "id": req_id,
                    },
                    status_code=400,
                )

        except ValueError as e:
            return JSONResponse(
                {"jsonrpc": "2.0", "error": {"code": -32602, "message": str(e)}, "id": req_id},
                status_code=400,
            )
        except Exception as e:
            logger.exception("Error handling MCP request")
            return JSONResponse(
                {"jsonrpc": "2.0", "error": {"code": -32603, "message": str(e)}, "id": req_id},
                status_code=500,
            )

    # Analysis store (shared across requests within the ASGI app)
    from lm_mcp.analysis import AnalysisStore, run_analysis, validate_workflow

    analysis_store = AnalysisStore(ttl_minutes=60)

    async def post_analyze(request: Request) -> Response:
        """Start an analysis workflow.

        Accepts JSON body with 'workflow' and 'arguments' fields.
        Returns 202 with analysis_id for async polling.
        """
        import asyncio

        try:
            body = await request.json()
        except (json.JSONDecodeError, Exception):
            return JSONResponse(
                {"error": "Invalid JSON body"}, status_code=400
            )

        workflow = body.get("workflow")
        if not workflow:
            return JSONResponse(
                {"error": "Missing 'workflow' field"}, status_code=400
            )

        try:
            validate_workflow(workflow)
        except ValueError as e:
            return JSONResponse({"error": str(e)}, status_code=400)

        arguments = body.get("arguments", {})
        req = analysis_store.create(workflow, arguments)

        # Run analysis in background
        asyncio.create_task(run_analysis(analysis_store, req.id))

        return JSONResponse(
            {"analysis_id": req.id, "status": "pending"},
            status_code=202,
        )

    async def get_analysis(request: Request) -> Response:
        """Poll analysis status and results.

        Returns the current state of an analysis request.
        """
        analysis_id = request.path_params["analysis_id"]
        req = analysis_store.get(analysis_id)

        if req is None:
            return JSONResponse(
                {"error": "Analysis not found"}, status_code=404
            )

        return JSONResponse(req.to_dict())

    async def webhook_alert(request: Request) -> Response:
        """Receive LM alert webhook and trigger RCA workflow.

        Accepts alert payload and starts an automatic RCA analysis.
        """
        import asyncio

        try:
            body = await request.json()
        except (json.JSONDecodeError, Exception):
            return JSONResponse(
                {"error": "Invalid JSON body"}, status_code=400
            )

        # Extract alert context for RCA
        device_id = body.get("deviceId")
        alert_id = body.get("alertId")
        arguments = {
            "hours_back": 4,
        }
        if device_id:
            arguments["device_id"] = device_id
        if alert_id:
            arguments["alert_id"] = alert_id

        req = analysis_store.create("rca_workflow", arguments)
        asyncio.create_task(run_analysis(analysis_store, req.id))

        return JSONResponse(
            {"analysis_id": req.id, "status": "pending"},
            status_code=202,
        )

    async def root(request: Request) -> Response:
        """Root endpoint with server info."""
        from lm_mcp import __version__

        return JSONResponse(
            {
                "name": "LogicMonitor MCP Server",
                "version": __version__,
                "transport": "http",
                "endpoints": {
                    "mcp": "/mcp",
                    "health": "/health",
                    "healthz": "/healthz",
                    "readyz": "/readyz",
                    "analyze": "/api/v1/analyze",
                    "analysis": "/api/v1/analysis/{id}",
                    "webhook_alert": "/api/v1/webhooks/alert",
                },
                "timestamp": datetime.now(timezone.utc).isoformat(),
            }
        )

    routes = [
        Route("/", root, methods=["GET"]),
        Route("/mcp", mcp_endpoint, methods=["POST"]),
        Route("/health", health, methods=["GET"]),
        Route("/healthz", healthz, methods=["GET"]),
        Route("/readyz", readyz, methods=["GET"]),
        Route("/api/v1/analyze", post_analyze, methods=["POST"]),
        Route("/api/v1/analysis/{analysis_id}", get_analysis, methods=["GET"]),
        Route("/api/v1/webhooks/alert", webhook_alert, methods=["POST"]),
    ]

    # Configure CORS middleware
    cors_origins = config.cors_origins_list
    middleware = []
    if cors_origins:
        middleware.append(
            Middleware(
                CORSMiddleware,
                allow_origins=cors_origins,
                allow_credentials=True,
                allow_methods=["*"],
                allow_headers=["*"],
            )
        )

    return Starlette(routes=routes, middleware=middleware)


async def create_http_server() -> None:
    """Create and run the HTTP server.

    Initializes the LogicMonitor client and starts Uvicorn.
    """
    import uvicorn

    from lm_mcp.auth import create_auth_provider
    from lm_mcp.client import LogicMonitorClient
    from lm_mcp.config import get_config
    from lm_mcp.server import _set_awx_client, _set_client, _set_eda_client
    from lm_mcp.session import get_session

    # Load config and create client
    config = get_config()
    auth = create_auth_provider(config)
    client = LogicMonitorClient(
        base_url=config.base_url,
        auth=auth,
        timeout=config.timeout,
        api_version=config.api_version,
        ingest_url=config.ingest_url,
    )
    _set_client(client)

    # Initialize AWX client if configured
    awx_client = None
    from lm_mcp.awx_config import get_awx_config

    awx_config = get_awx_config()
    if awx_config is not None:
        from lm_mcp.client.awx import AwxClient

        awx_client = AwxClient(
            base_url=awx_config.url,
            token=awx_config.token,
            timeout=awx_config.timeout,
            max_retries=awx_config.max_retries,
            verify_ssl=awx_config.verify_ssl,
        )
        _set_awx_client(awx_client)

    # Initialize EDA client if configured
    eda_client = None
    from lm_mcp.eda_config import get_eda_config

    eda_config = get_eda_config()
    if eda_config is not None:
        from lm_mcp.client.eda import EdaClient

        eda_client = EdaClient(
            base_url=eda_config.url,
            token=eda_config.token,
            timeout=eda_config.timeout,
            max_retries=eda_config.max_retries,
            verify_ssl=eda_config.verify_ssl,
        )
        _set_eda_client(eda_client)

    # Initialize session with config settings
    if config.session_enabled:
        session = get_session()
        session.max_history_size = config.session_history_size

    # Create ASGI app
    app = create_asgi_app()

    # Configure Uvicorn
    uvicorn_config = uvicorn.Config(
        app,
        host=config.http_host,
        port=config.http_port,
        log_level="info",
    )

    server = uvicorn.Server(uvicorn_config)

    logger.info(f"Starting HTTP server on {config.http_host}:{config.http_port}")

    try:
        await server.serve()
    finally:
        if eda_client is not None:
            await eda_client.close()
        if awx_client is not None:
            await awx_client.close()
        await client.close()
