"""
Async SQLAlchemy engine, session factory, and database utilities.

Engine uses asyncpg driver for PostgreSQL 16.
All database interactions in the application MUST use the async session.
"""

from collections.abc import AsyncGenerator

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

from app.config import settings

# ── Engine ─────────────────────────────────────────────────────────────────────
# pool_pre_ping: verifies connections are alive before using from pool
# pool_size: number of persistent connections
# max_overflow: additional connections allowed above pool_size under load
engine = create_async_engine(
    settings.DATABASE_URL,
    echo=settings.APP_DEBUG,
    pool_pre_ping=True,
    pool_recycle=1800,
    pool_timeout=15,
    pool_size=settings.DATABASE_POOL_SIZE,
    max_overflow=settings.DATABASE_MAX_OVERFLOW,
)

# ── Session Factory ────────────────────────────────────────────────────────────
# expire_on_commit=False: keeps ORM objects usable after session.commit()
# This is important for async usage where lazy loading is not available.
AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autoflush=False,
    autocommit=False,
)


async def get_db() -> AsyncGenerator[AsyncSession, None]:
    """
    FastAPI dependency that provides a database session per request.

    Usage in route handlers:
        async def my_endpoint(db: AsyncSession = Depends(get_db)):
            ...
    """
    async with AsyncSessionLocal() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()
