"""Application factory and lifespan wiring for the transcription app."""

from __future__ import annotations

import asyncio
from contextlib import asynccontextmanager
from contextlib import suppress

from fastapi import FastAPI

from .api.errors import register_error_handlers
from .api.health import router as health_router
from .config import configure_logging
from .config import get_settings
from .db import create_all
from .db import dispose_database_runtime
from .db import initialize_database_runtime
from .services import ServiceBundle
from .ui import register_pages
from .worker import run_worker_loop


def _start_worker(app: FastAPI) -> None:
    stop_event = asyncio.Event()
    wake_queue: asyncio.Queue[None] = asyncio.Queue()
    worker_task = asyncio.create_task(
        run_worker_loop(
            session_factory=app.state.runtime.session_factory,
            stop_event=stop_event,
            wake_queue=wake_queue,
            poll_interval_seconds=1.0,
        )
    )
    wake_queue.put_nowait(None)
    app.state.worker_stop_event = stop_event
    app.state.worker_wake_queue = wake_queue
    app.state.worker_task = worker_task


async def _stop_worker(app: FastAPI) -> None:
    stop_event = getattr(app.state, "worker_stop_event", None)
    wake_queue = getattr(app.state, "worker_wake_queue", None)
    worker_task = getattr(app.state, "worker_task", None)

    if stop_event is not None:
        stop_event.set()
    if wake_queue is not None:
        wake_queue.put_nowait(None)
    if worker_task is not None:
        try:
            await asyncio.wait_for(worker_task, timeout=2.0)
        except TimeoutError:
            worker_task.cancel()
            with suppress(asyncio.CancelledError):
                await worker_task


@asynccontextmanager
async def _lifespan(app: FastAPI):
    configure_logging()

    settings = get_settings()
    app.state.settings = settings
    app.state.services = ServiceBundle()
    app.state.runtime = initialize_database_runtime(settings=settings)

    if settings.should_bootstrap_schema:
        await create_all(engine=app.state.runtime.engine)

    settings.upload_dir.mkdir(parents=True, exist_ok=True)
    settings.prompt_dir.mkdir(parents=True, exist_ok=True)

    _start_worker(app)
    try:
        yield
    finally:
        await _stop_worker(app)
        await dispose_database_runtime()


def create_app() -> FastAPI:
    """Create and configure the FastAPI application."""
    app = FastAPI(title="Transcription", lifespan=_lifespan)
    register_error_handlers(app)
    register_pages(app)
    app.include_router(health_router)
    return app
