"""Async database session management."""

from __future__ import annotations

from typing import AsyncGenerator

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

from kredo_drift.server.config import get_settings

_engine = None
_session_factory = None


async def init_db():
    """Initialize the async database engine and create tables."""
    global _engine, _session_factory
    settings = get_settings()
    # WHY conditional pool args: SQLite doesn't support connection pooling
    # (pool_size/max_overflow), but PostgreSQL needs them for concurrency.
    engine_kwargs = {"echo": settings.database_echo}
    if "sqlite" not in settings.database_url:
        engine_kwargs["pool_size"] = 10
        engine_kwargs["max_overflow"] = 20
    _engine = create_async_engine(settings.database_url, **engine_kwargs)
    # WHY expire_on_commit=False: After committing a baseline or test record,
    # the route handler often accesses the object's attributes (e.g., content_hash)
    # in the response. With the default expire_on_commit=True, SQLAlchemy would
    # lazily reload the object, requiring an extra DB roundtrip (and failing in
    # async context without explicit refresh). Disabling expiry avoids this.
    _session_factory = async_sessionmaker(
        _engine, class_=AsyncSession, expire_on_commit=False,
    )

    # Create tables (use Alembic for production migrations)
    from kredo_drift.server.db.models import Base
    from kredo_drift.server.db import drift_models  # noqa: F401 — register models
    async with _engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)


async def close_db():
    """Close the database engine."""
    global _engine
    if _engine:
        await _engine.dispose()
        _engine = None


async def get_db() -> AsyncGenerator[AsyncSession, None]:
    """Dependency that provides an async database session."""
    if _session_factory is None:
        await init_db()
    async with _session_factory() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise
