"""Tests for the Setting key-value model, incl. the create-race recovery."""

from sqlalchemy.exc import IntegrityError

from shopdb.core.models import Setting
from shopdb.extensions import db as _db


def test_set_creates_then_updates(db):
    """set() creates a missing key, then updates the existing row in place."""
    created = Setting.set('greeting', 'hello', category='site')
    assert created.settingid is not None
    assert Setting.get('greeting') == 'hello'

    Setting.set('greeting', 'goodbye')
    assert Setting.get('greeting') == 'goodbye'

    # Only one row for the key (update, not a second insert).
    assert Setting.query.filter_by(key='greeting').count() == 1


def test_set_boolean_roundtrip(db):
    """Booleans store as canonical strings and read back typed."""
    Setting.set('flag', True, valuetype='boolean')
    assert Setting.get('flag') is True
    Setting.set('flag', False)
    assert Setting.get('flag') is False


def test_set_recovers_from_create_race(db, monkeypatch):
    """A concurrent insert of the same key makes our commit raise IntegrityError;
    set() rolls back, re-fetches the winner's row, and applies our value."""
    real_commit = _db.session.commit
    calls = {'n': 0}

    def flaky_commit():
        calls['n'] += 1
        if calls['n'] == 1:
            # Drop our own pending insert, then persist the "winner" row exactly
            # as a racing transaction would have, and simulate the unique-key
            # violation our losing INSERT would raise.
            _db.session.rollback()
            winner = Setting(key='racekey', value='winner', valuetype='string')
            _db.session.add(winner)
            real_commit()
            raise IntegrityError('INSERT INTO settings', {}, Exception('duplicate key'))
        return real_commit()

    monkeypatch.setattr(_db.session, 'commit', flaky_commit)

    result = Setting.set('racekey', 'mine')

    # Recovery path ran: the row exists once and carries our value.
    assert result is not None
    assert Setting.get('racekey') == 'mine'
    assert Setting.query.filter_by(key='racekey').count() == 1
