"""FastAPI application entrypoint."""

from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from sqlalchemy import text

from gnexus_creds.api import admin_router
from gnexus_creds.api import router as api_router
from gnexus_creds.config import get_settings
from gnexus_creds.db import SessionLocal
from gnexus_creds.errors import AppError, app_error_handler, validation_error_handler
from gnexus_creds.logging import configure_logging
from gnexus_creds.mcp import router as mcp_router
from gnexus_creds.mcp_protocol import create_mcp_protocol_server
from gnexus_creds.oauth import router as auth_router
from gnexus_creds.oauth import webhook_router

PROJECT_ROOT = Path(__file__).resolve().parent.parent
FRONTEND_DIST = PROJECT_ROOT / "frontend" / "dist"
UI_KIT_ASSETS = PROJECT_ROOT / "frontend" / "node_modules" / "gnexus-ui-kit" / "dist" / "assets"


def create_app() -> FastAPI:
    configure_logging()
    settings = get_settings()
    mcp_protocol_server = create_mcp_protocol_server()
    mcp_protocol_app = mcp_protocol_server.streamable_http_app()

    @asynccontextmanager
    async def lifespan(_: FastAPI):
        async with mcp_protocol_server.session_manager.run():
            yield

    app = FastAPI(title=settings.app_name, version="0.1.0", lifespan=lifespan)
    app.state.mcp_protocol_server = mcp_protocol_server
    app.add_exception_handler(AppError, app_error_handler)
    app.add_exception_handler(RequestValidationError, validation_error_handler)
    app.include_router(auth_router)
    app.include_router(webhook_router)
    app.include_router(api_router)
    app.include_router(admin_router)
    app.include_router(mcp_router)
    app.mount("/mcp-protocol", mcp_protocol_app, name="mcp-protocol")

    @app.get("/health", tags=["system"])
    async def health() -> dict[str, str]:
        return {"status": "ok"}

    @app.get("/ready", tags=["system"])
    async def ready() -> dict[str, str]:
        with SessionLocal() as db:
            db.execute(text("select 1"))
        return {"status": "ok"}

    if FRONTEND_DIST.exists():
        ui_kit_fonts_dir = UI_KIT_ASSETS / "fonts"
        if ui_kit_fonts_dir.exists():
            app.mount("/assets/fonts", StaticFiles(directory=ui_kit_fonts_dir), name="ui-kit-fonts")

        assets_dir = FRONTEND_DIST / "assets"
        if assets_dir.exists():
            app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")

        @app.get("/", include_in_schema=False)
        async def index() -> FileResponse:
            return FileResponse(FRONTEND_DIST / "index.html")

        @app.get("/{path:path}", include_in_schema=False)
        async def spa_fallback(path: str) -> FileResponse:
            if path.startswith(("api/", "auth/", "mcp/", "mcp-protocol/", "webhooks/")):
                raise AppError("not_found", "Not found.", status_code=404)
            return FileResponse(FRONTEND_DIST / "index.html")

    return app


app = create_app()
