"""Database engine and session management."""

from collections.abc import AsyncGenerator
import logging

from sqlalchemy import event
from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine

from ..config import settings

_LOGGER = logging.getLogger(__name__)

_engine: AsyncEngine | None = None


def _install_sqlite_pragmas(engine: AsyncEngine) -> None:
    """Apply production-grade SQLite PRAGMAs on every new connection.

    WAL mode lets readers and writers work concurrently without blocking;
    ``busy_timeout`` gives contending writers a chance instead of instant
    SQLITE_BUSY; ``foreign_keys`` enforces the FK constraints declared in the
    models (SQLite disables them by default); ``synchronous=NORMAL`` is a
    safe-by-default durability trade-off that is standard in WAL mode.
    """

    @event.listens_for(engine.sync_engine, "connect")
    def _pragmas(dbapi_conn, _record):  # pragma: no cover — driver hook
        cur = dbapi_conn.cursor()
        try:
            cur.execute("PRAGMA journal_mode=WAL")
            cur.execute("PRAGMA synchronous=NORMAL")
            cur.execute("PRAGMA foreign_keys=ON")
            cur.execute("PRAGMA busy_timeout=10000")
            cur.execute("PRAGMA temp_store=MEMORY")
        finally:
            cur.close()


def get_engine() -> AsyncEngine:
    global _engine
    if _engine is None:
        url = settings.effective_database_url
        connect_args: dict = {}
        if url.startswith("sqlite"):
            connect_args["timeout"] = 30
        _engine = create_async_engine(
            url,
            echo=settings.debug,
            pool_pre_ping=True,
            connect_args=connect_args,
        )
        if url.startswith("sqlite"):
            _install_sqlite_pragmas(_engine)
        _LOGGER.info("Database engine initialized: %s", url.split("://", 1)[0])
    return _engine


async def init_db() -> None:
    engine = get_engine()
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)


async def get_session() -> AsyncGenerator[AsyncSession, None]:
    engine = get_engine()
    async with AsyncSession(engine) as session:
        yield session


async def dispose_engine() -> None:
    """Close the engine's connection pool. Call during graceful shutdown."""
    global _engine
    if _engine is not None:
        await _engine.dispose()
        _engine = None
