from typing import Optional, cast

from werkzeug.exceptions import PreconditionFailed

from backend.database.base import db
from backend.database.models import AdminSettings
from backend.models.admin_config import AdminConfig


def upsert_admin_settings(user: str, settings: dict) -> AdminConfig:
    """
    Insert or update admin settings for a user using optimistic locking.
    There should only be one settings record per user.
    """
    with db.session.begin():
        existing = db.session.query(AdminSettings).filter(AdminSettings.user == user).first()
        if existing is None:
            inserted_settings = dict(settings)
            inserted_settings["version"] = 1
            db.session.add(AdminSettings(user=user, version=1, settings=inserted_settings))
            return cast(AdminConfig, inserted_settings)

        version = settings.get("version")
        if not isinstance(version, int) or not version:
            raise ValueError("settings.version is required")

        next_version = version + 1
        updated_settings = dict(settings)
        updated_settings["version"] = next_version

        updated_rows = (
            db.session.query(AdminSettings)
            .filter(AdminSettings.user == user, AdminSettings.version == version)
            .update(
                {AdminSettings.settings: updated_settings, AdminSettings.version: next_version},
                synchronize_session=False,
            )
        )

        if updated_rows == 0:
            raise PreconditionFailed("admin settings changed concurrently")

        return cast(AdminConfig, updated_settings)


def get_admin_config(user: str) -> Optional[AdminConfig]:
    with db.session.begin():
        ret = db.session.query(AdminSettings).filter(AdminSettings.user == user).first()
        if not ret or not isinstance(ret.settings, dict):
            return None
        config = dict(ret.settings)
        config["version"] = ret.version
        return cast(AdminConfig, config)
