"""Single source for SystemSetting reads/writes.

Services used to hand-roll ``select(SystemSetting).where(key == ...)`` +
``scalar_one_or_none`` (plus a near-identical get-or-create upsert) in a dozen
places. These helpers centralise that. ``upsert_setting`` never commits — the
caller owns the transaction. ``updated_at`` is managed by the model's
``onupdate`` hook, so callers don't set it.
"""

from __future__ import annotations

from collections.abc import Iterable

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.settings import SystemSetting


async def get_setting(db: AsyncSession, key: str) -> SystemSetting | None:
    """Return the SystemSetting row for ``key``, or None if unset."""
    result = await db.execute(select(SystemSetting).where(SystemSetting.key == key))
    return result.scalar_one_or_none()


async def get_value(db: AsyncSession, key: str, default: str | None = None) -> str | None:
    """Return the stored value for ``key``, or ``default`` if unset."""
    setting = await get_setting(db, key)
    return setting.value if setting is not None else default


async def get_map(db: AsyncSession, keys: Iterable[str]) -> dict[str, str]:
    """Return a {key: value} map for the given keys that exist."""
    result = await db.execute(select(SystemSetting).where(SystemSetting.key.in_(list(keys))))
    return {s.key: s.value for s in result.scalars().all()}


async def upsert_setting(db: AsyncSession, key: str, value: str) -> SystemSetting:
    """Create or update a setting. Does NOT commit; caller controls the transaction."""
    setting = await get_setting(db, key)
    if setting is None:
        setting = SystemSetting(key=key, value=value)
        db.add(setting)
    else:
        setting.value = value
    return setting
