import asyncio
import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse

from backend.bootstrap.container import build_container
from backend.bootstrap.logging import configure_logging
from backend.bootstrap.settings import Settings
from backend.database.migrations import MIGRATIONS, MigrationRunner
from backend.http.errors import install_error_handlers
from backend.http.request_context import install_request_context
from backend.http.router import api_router
from backend.http.security import install_security_headers
from backend.jobs.operations import run_operations_scheduler

logger = logging.getLogger(__name__)


def create_application(settings: Settings | None = None) -> FastAPI:
    runtime = settings or Settings.from_environment()
    container = build_container(runtime)

    @asynccontextmanager
    async def lifespan(_application: FastAPI):
        runtime.data_directory.mkdir(parents=True, exist_ok=True)
        configure_logging(runtime.log_level, runtime.log_file)
        MigrationRunner(container.database).upgrade(MIGRATIONS)
        database_status = container.database_status.get()
        logger.info(
            "Application started",
            extra={
                "event": "application.started",
                "context": {
                    "environment": runtime.environment,
                    "schema_version": database_status.schema_version,
                },
            },
        )
        stop = asyncio.Event()
        task = None
        if runtime.environment != "test":
            task = asyncio.create_task(run_operations_scheduler(container.operations, stop))
        try:
            yield
        finally:
            if task:
                stop.set()
                await task

    application = FastAPI(
        title="小白复盘",
        version="0.1.0",
        docs_url="/api/docs" if runtime.debug else None,
        redoc_url=None,
        lifespan=lifespan,
    )
    application.state.settings = runtime
    application.state.container = container
    install_request_context(application)
    install_security_headers(application)
    install_error_handlers(application)
    application.include_router(api_router, prefix="/api")
    if runtime.frontend_dist_directory.joinpath("index.html").is_file():
        frontend_root = runtime.frontend_dist_directory.resolve()

        @application.get("/{requested_path:path}", include_in_schema=False)
        def frontend(requested_path: str) -> FileResponse:
            if requested_path == "api" or requested_path.startswith("api/"):
                raise HTTPException(status_code=404)
            candidate = frontend_root.joinpath(requested_path).resolve()
            if candidate.is_relative_to(frontend_root) and candidate.is_file():
                response = FileResponse(candidate)
                if candidate.parent.name == "assets":
                    response.headers["Cache-Control"] = "public,max-age=31536000,immutable"
                return response
            return FileResponse(frontend_root / "index.html", headers={"Cache-Control": "no-cache"})

    return application
