"""
Async database session management.

Uses SQLAlchemy's async engine with asyncpg — the API serving layer is
async (FastAPI), so the DB driver must be too, or every DB call would
block an event loop thread. The Celery workers, which are synchronous
processes, use a separate sync engine (see app/db/sync_session.py) since
mixing async DB calls into a sync Celery task is more trouble than it's
worth.
"""

from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from app.core.config import get_settings
from app.core.logging_config import get_logger

settings = get_settings()
logger = get_logger(__name__)

_engine = create_async_engine(
    settings.DATABASE_URL,
    pool_size=settings.DB_POOL_SIZE,
    max_overflow=settings.DB_MAX_OVERFLOW,
    echo=settings.DB_ECHO,
    pool_pre_ping=True,  # detects stale connections before using them —
                         # important after VPS network blips or DB restarts
)

_session_factory = async_sessionmaker(_engine, expire_on_commit=False, class_=AsyncSession)


async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
    """FastAPI dependency — one session per request, always closed cleanly."""
    async with _session_factory() as session:
        try:
            yield session
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()


@asynccontextmanager
async def db_session_context():
    """For use outside of FastAPI's dependency injection (e.g. startup scripts)."""
    async with _session_factory() as session:
        try:
            yield session
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()


async def check_db_connection() -> bool:
    try:
        async with _engine.connect() as conn:
            await conn.run_sync(lambda c: None)
        return True
    except Exception as exc:
        logger.error(f"Database health check failed: {exc}")
        return False


async def dispose_engine() -> None:
    """Called on application shutdown to close all pooled connections cleanly."""
    await _engine.dispose()
