"""Starlette application builder for Outpost9 agents."""

import json
import inspect
from typing import Any, Optional
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import JSONResponse, StreamingResponse

from .decorators import get_routes
from .streaming import create_sse_stream
from ._internal.auto_logging import set_run_id
from ._internal.http import notify_run_complete


async def _create_handler(route_info: dict[str, Any]):
    """Create a Starlette request handler for a route."""
    func = route_info["func"]

    async def handler(request: Request):
        # Parse request arguments
        kwargs = {}

        # Get query params
        kwargs.update(dict(request.query_params))

        # Get JSON body if present
        if request.method in ("POST", "PUT", "PATCH"):
            try:
                body = await request.json()
                if isinstance(body, dict):
                    kwargs.update(body)
            except json.JSONDecodeError:
                pass

        # Get path params
        kwargs.update(request.path_params)

        # Check if client wants SSE streaming
        accept = request.headers.get("accept", "")
        wants_sse = "text/event-stream" in accept

        if wants_sse:
            # Stream response via SSE
            return StreamingResponse(
                create_sse_stream(func, **kwargs),
                media_type="text/event-stream",
                headers={
                    "Cache-Control": "no-cache",
                    "Connection": "keep-alive",
                    "X-Accel-Buffering": "no",
                },
            )
        else:
            # Direct JSON response
            result = await func(**kwargs)
            return JSONResponse(result)

    return handler


def create_app() -> Starlette:
    """
    Create a Starlette application with all registered routes.

    Returns:
        Configured Starlette application
    """
    routes_registry = get_routes()
    starlette_routes = []

    for path, route_info in routes_registry.items():
        # Create handler for this route
        async def make_handler(ri=route_info):
            return await _create_handler(ri)

        # We need to capture route_info in a closure
        func = route_info["func"]
        methods = route_info["methods"]
        name = route_info["name"]

        async def handler_wrapper(request: Request, _func=func, _info=route_info):
            # Extract per-request run ID from gateway header
            run_id = request.headers.get("x-run-id")
            if run_id:
                set_run_id(run_id)

            kwargs = {}
            kwargs.update(dict(request.query_params))
            if request.method in ("POST", "PUT", "PATCH"):
                try:
                    body = await request.json()
                    if isinstance(body, dict):
                        kwargs.update(body)
                except json.JSONDecodeError:
                    pass
            kwargs.update(request.path_params)

            accept = request.headers.get("accept", "")
            wants_sse = "text/event-stream" in accept

            if wants_sse:
                return StreamingResponse(
                    create_sse_stream(_func, run_id=run_id, **kwargs),
                    media_type="text/event-stream",
                    headers={
                        "Cache-Control": "no-cache",
                        "Connection": "keep-alive",
                        "X-Accel-Buffering": "no",
                    },
                )
            else:
                result = await _func(**kwargs)
                response = JSONResponse(result)
                # Notify Worker that the handler completed
                if run_id:
                    notify_run_complete(run_id, response.status_code)
                return response

        starlette_routes.append(
            Route(path, endpoint=handler_wrapper, methods=methods, name=name)
        )

    # Add health check
    async def health(request: Request):
        return JSONResponse({"status": "ok", "routes": list(routes_registry.keys())})

    starlette_routes.append(Route("/health", endpoint=health, methods=["GET"]))

    return Starlette(routes=starlette_routes)


def run_server(
    host: str = "0.0.0.0",
    port: int = 8000,
    reload: bool = False,
) -> None:
    """
    Run the Outpost9 agent server.

    Args:
        host: Host to bind to (default: 0.0.0.0)
        port: Port to listen on (default: 8000)
        reload: Enable auto-reload for development
    """
    import uvicorn

    app = create_app()
    uvicorn.run(app, host=host, port=port, reload=reload)
