"""FastAPI application factory for OrionBelt Semantic Layer."""

from __future__ import annotations

import logging
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

import uvicorn
from fastapi import FastAPI
from fastapi.responses import Response

from orionbelt import __version__
from orionbelt.api.deps import init_session_manager, reset_session_manager
from orionbelt.api.middleware import (
    RequestBodyLimitMiddleware,
    RequestTimingMiddleware,
    SecurityHeadersMiddleware,
)
from orionbelt.api.routers import convert, dialects, reference, sessions
from orionbelt.api.schemas import HealthResponse
from orionbelt.service.session_manager import SessionManager
from orionbelt.settings import Settings


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
    """Start/stop the SessionManager alongside the application."""
    settings: Settings = app.state.settings
    mgr = SessionManager(
        ttl_seconds=settings.session_ttl_seconds,
        cleanup_interval=settings.session_cleanup_interval,
    )
    mgr.start()
    init_session_manager(mgr, disable_session_list=settings.disable_session_list)
    try:
        yield
    finally:
        mgr.stop()
        reset_session_manager()


def create_app(settings: Settings | None = None) -> FastAPI:
    """Create and configure the FastAPI application."""
    if settings is None:
        settings = Settings()

    app = FastAPI(
        title="OrionBelt Semantic Layer",
        description="Compiles YAML semantic models into analytical SQL across multiple dialects.",
        version=__version__,
        lifespan=lifespan,
    )
    app.state.settings = settings

    # Middleware (order matters: last added = first to execute)
    app.add_middleware(RequestTimingMiddleware)
    app.add_middleware(SecurityHeadersMiddleware)
    app.add_middleware(RequestBodyLimitMiddleware)

    # Session-scoped endpoints
    app.include_router(sessions.router, prefix="/sessions", tags=["sessions"])

    app.include_router(convert.router, prefix="/convert", tags=["convert"])

    app.include_router(dialects.router, prefix="/dialects", tags=["dialects"])

    app.include_router(reference.router, prefix="/reference", tags=["reference"])

    @app.get("/health", response_model=HealthResponse, tags=["health"])
    async def health() -> HealthResponse:
        return HealthResponse(status="ok", version=__version__)

    @app.get("/robots.txt", include_in_schema=False)
    async def robots_txt() -> Response:
        return Response("User-agent: *\nAllow: /\n", media_type="text/plain")

    # Mount Gradio UI at /ui when the 'ui' extra is installed
    try:
        import gradio as gr

        from orionbelt.ui.app import _CSS, _DARK_MODE_INIT_JS, create_blocks

        api_url = f"http://localhost:{settings.effective_port}"
        demo = create_blocks(default_api_url=api_url)
        app = gr.mount_gradio_app(app, demo, path="/ui", css=_CSS, js=_DARK_MODE_INIT_JS)
    except ImportError:
        pass  # gradio not installed — skip UI mount

    return app


def main() -> None:
    """Run the REST API server using settings from environment / .env file."""
    settings = Settings()

    logging.basicConfig(level=settings.log_level.upper())
    logger = logging.getLogger("orionbelt.api")
    logger.info(
        "OrionBelt API Server v%s starting (host=%s, port=%d)",
        __version__,
        settings.api_server_host,
        settings.effective_port,
    )

    uvicorn.run(
        "orionbelt.api.app:create_app",
        factory=True,
        host=settings.api_server_host,
        port=settings.effective_port,
        log_level=settings.log_level.lower(),
    )
