"""
資料庫連線管理(SQLAlchemy 2.0 async)。

設計重點:
  1. 使用 async 引擎,FastAPI 是 async 框架,不用 async 等於浪費效能。
  2. AsyncSession 透過 FastAPI dependency injection 注入到 endpoint。
  3. 每個請求一個 session,請求結束自動關閉。
"""

from typing import AsyncGenerator

from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase

from app.config import settings


# ---------------------------------------------------------------------------
# Base:所有資料表 Model 都繼承這個類別
# ---------------------------------------------------------------------------
class Base(DeclarativeBase):
    """SQLAlchemy 的 declarative base。

    所有資料表 Model(例如 User)都繼承 Base,
    Alembic 會用 Base.metadata 自動偵測資料表變化、產生 migration。
    """
    pass


# ---------------------------------------------------------------------------
# 引擎(Engine):連線池管理者,整個應用程式共用一個
# ---------------------------------------------------------------------------
# SQLite(開發模式)用單一連線,不適用 pool_size/max_overflow
# PostgreSQL(Docker / 上線)才需要連線池參數
_is_sqlite = settings.DATABASE_URL.startswith("sqlite")
_engine_kwargs: dict = {
    "echo": False,           # 開 True 會把所有 SQL 印到 console,debug 時很有用
}
if not _is_sqlite:
    _engine_kwargs.update(
        pool_pre_ping=True,   # 每次取連線前 ping 一下,避免拿到斷線的連線
        pool_size=10,         # 預設連線池大小
        max_overflow=20,      # 尖峰時可以多開的連線數
    )

engine: AsyncEngine = create_async_engine(settings.DATABASE_URL, **_engine_kwargs)

# ---------------------------------------------------------------------------
# Session 工廠:每個請求 new 一個 AsyncSession
# ---------------------------------------------------------------------------
AsyncSessionLocal: async_sessionmaker[AsyncSession] = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,  # commit 後物件還能讀(否則要重抓)
    autoflush=False,
)


# ---------------------------------------------------------------------------
# FastAPI dependency:每個請求拿到一個 session
# ---------------------------------------------------------------------------
async def get_db() -> AsyncGenerator[AsyncSession, None]:
    """提供 AsyncSession 給 endpoint 使用。

    用法(在 endpoint 函式參數):
        async def some_endpoint(db: AsyncSession = Depends(get_db)):
            ...

    yield 後面的 async with 自動處理 commit/rollback/close。
    """
    async with AsyncSessionLocal() as session:
        try:
            yield session
        except Exception:
            # 出錯就回滾,避免半套資料留在資料庫
            await session.rollback()
            raise
        # 注意:session.commit() 由業務邏輯自己決定何時呼叫,
        # 不在這裡自動 commit,避免「只讀請求也 commit」的浪費。
