"""FastAPI app factory for all subsystems."""

from __future__ import annotations

import platform
import time
from pathlib import Path

from fastapi import FastAPI
from fastapi.responses import FileResponse, JSONResponse, Response
from fastapi.staticfiles import StaticFiles

from common.middleware import (
    add_cors,
    add_exception_handlers,
    add_rate_limiting,
    add_request_logging,
)
from common.types import HealthResponse

_start_time = time.time()


def create_app(
    title: str,
    version: str,
    cors_origins: list[str] | None = None,
    rate_limit: str | None = None,
) -> FastAPI:
    """Create a base app with shared middleware and health/version endpoint."""
    app = FastAPI(title=title, version=version, docs_url="/api/swagger", redoc_url="/api/redoc")

    add_exception_handlers(app)
    add_cors(app, origins=cors_origins)
    add_request_logging(app)
    if rate_limit:
        add_rate_limiting(app, default_limit=rate_limit)

    @app.get("/api/health")
    async def health() -> dict:
        return {
            "status": "ok",
            "version": version,
            "uptime_seconds": round(time.time() - _start_time, 1),
            "platform": platform.system(),
            "python": platform.python_version(),
        }

    return app


def mount_spa(app: FastAPI, dist_dir: Path) -> None:
    """Mount built React SPA assets and add client-side routing fallback.

    Must be called LAST, after all API/MCP mounts, so the catch-all
    does not shadow real endpoints.
    """
    index_path = dist_dir / "index.html"
    if not index_path.exists():
        return

    # Serve Vite's hashed static assets (JS/CSS/images)
    assets_dir = dist_dir / "assets"
    if assets_dir.exists():
        app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="spa-assets")

    @app.get("/{full_path:path}", include_in_schema=False)
    async def spa_fallback(full_path: str) -> Response:
        # Never intercept API, MCP, or OTLP ingest paths
        if full_path.startswith(("api/", "mcp/", "v1/")):
            return JSONResponse({"detail": "Not Found"}, status_code=404)
        # Serve actual files from dist if they exist (e.g. favicon, manifest)
        file_path = dist_dir / full_path
        if full_path and file_path.exists() and file_path.is_file():
            return FileResponse(file_path)
        # Everything else gets index.html for client-side routing
        return FileResponse(index_path)
