"""DB helper utilities: upsert, bulk operations, health check."""
from __future__ import annotations

from typing import Any

from sqlalchemy import select, text
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.ext.asyncio import AsyncSession

from libs.db.models import Base


async def upsert(
    session: AsyncSession,
    model: type[Base],
    values: dict[str, Any],
    index_elements: list[str],
    update_columns: list[str] | None = None,
) -> None:
    """Insert or update a single row using PostgreSQL ON CONFLICT."""
    stmt = insert(model).values(**values)
    if update_columns:
        update_dict = {col: getattr(stmt.excluded, col) for col in update_columns}
        stmt = stmt.on_conflict_do_update(
            index_elements=index_elements, set_=update_dict
        )
    else:
        stmt = stmt.on_conflict_do_nothing(index_elements=index_elements)
    await session.execute(stmt)


async def bulk_upsert(
    session: AsyncSession,
    model: type[Base],
    rows: list[dict[str, Any]],
    index_elements: list[str],
    update_columns: list[str] | None = None,
) -> int:
    """Bulk upsert; returns number of rows processed."""
    if not rows:
        return 0
    stmt = insert(model).values(rows)
    if update_columns:
        update_dict = {col: getattr(stmt.excluded, col) for col in update_columns}
        stmt = stmt.on_conflict_do_update(
            index_elements=index_elements, set_=update_dict
        )
    else:
        stmt = stmt.on_conflict_do_nothing(index_elements=index_elements)
    await session.execute(stmt)
    return len(rows)


async def get_or_create(
    session: AsyncSession,
    model: type[Base],
    pk_value: Any,
    pk_column: str,
    defaults: dict[str, Any],
) -> tuple[Any, bool]:
    """Return (instance, created). Upserts if not found."""
    col = getattr(model, pk_column)
    result = await session.execute(select(model).where(col == pk_value))
    row = result.scalar_one_or_none()
    if row is not None:
        return row, False
    instance = model(**{pk_column: pk_value, **defaults})
    session.add(instance)
    await session.flush()
    return instance, True


async def health_check(session: AsyncSession) -> bool:
    """Return True if DB is reachable."""
    try:
        await session.execute(text("SELECT 1"))
        return True
    except Exception:
        return False
