"""Tests for SyncClockStore — CRUD, speed clamping, name uniqueness."""

import pytest

from wled_controller.storage.sync_clock import SyncClock
from wled_controller.storage.sync_clock_store import SyncClockStore


@pytest.fixture
def store(tmp_db) -> SyncClockStore:
    return SyncClockStore(tmp_db)


# ---------------------------------------------------------------------------
# SyncClock model
# ---------------------------------------------------------------------------


class TestSyncClockModel:
    def test_to_dict_round_trip(self, make_sync_clock):
        clock = make_sync_clock(name="RT", speed=2.5, description="test", tags=["a"])
        data = clock.to_dict()
        restored = SyncClock.from_dict(data)
        assert restored.id == clock.id
        assert restored.name == "RT"
        assert restored.speed == 2.5
        assert restored.description == "test"
        assert restored.tags == ["a"]

    def test_from_dict_defaults(self):
        data = {
            "id": "sc_1",
            "name": "Default",
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        clock = SyncClock.from_dict(data)
        assert clock.speed == 1.0
        assert clock.description is None
        assert clock.tags == []


# ---------------------------------------------------------------------------
# SyncClockStore CRUD
# ---------------------------------------------------------------------------


class TestSyncClockStoreCRUD:
    def test_create_clock(self, store):
        c = store.create_clock(name="Clock A")
        assert c.id.startswith("sc_")
        assert c.name == "Clock A"
        assert c.speed == 1.0
        assert store.count() == 1

    def test_create_clock_with_options(self, store):
        c = store.create_clock(
            name="Fast", speed=5.0, description="speedy", tags=["anim"]
        )
        assert c.speed == 5.0
        assert c.description == "speedy"
        assert c.tags == ["anim"]

    def test_get_clock(self, store):
        created = store.create_clock(name="Get")
        got = store.get_clock(created.id)
        assert got.name == "Get"

    def test_get_all_clocks(self, store):
        store.create_clock("A")
        store.create_clock("B")
        assert len(store.get_all_clocks()) == 2

    def test_delete_clock(self, store):
        c = store.create_clock("Del")
        store.delete_clock(c.id)
        assert store.count() == 0

    def test_delete_not_found(self, store):
        with pytest.raises(ValueError, match="not found"):
            store.delete_clock("nope")

    def test_update_clock(self, store):
        c = store.create_clock(name="Old", speed=1.0)
        updated = store.update_clock(c.id, name="New", speed=3.0)
        assert updated.name == "New"
        assert updated.speed == 3.0

    def test_update_clock_partial(self, store):
        c = store.create_clock(name="Keep", speed=2.0, description="orig")
        updated = store.update_clock(c.id, speed=4.0)
        assert updated.name == "Keep"
        assert updated.speed == 4.0
        assert updated.description == "orig"


# ---------------------------------------------------------------------------
# Speed clamping
# ---------------------------------------------------------------------------


class TestSpeedClamping:
    def test_create_clamps_low(self, store):
        c = store.create_clock(name="Low", speed=0.01)
        assert c.speed == 0.1

    def test_create_clamps_high(self, store):
        c = store.create_clock(name="High", speed=100.0)
        assert c.speed == 10.0

    def test_update_clamps_low(self, store):
        c = store.create_clock(name="UL", speed=1.0)
        updated = store.update_clock(c.id, speed=-5.0)
        assert updated.speed == 0.1

    def test_update_clamps_high(self, store):
        c = store.create_clock(name="UH", speed=1.0)
        updated = store.update_clock(c.id, speed=999.0)
        assert updated.speed == 10.0


# ---------------------------------------------------------------------------
# Name uniqueness
# ---------------------------------------------------------------------------


class TestSyncClockNameUniqueness:
    def test_duplicate_name_create(self, store):
        store.create_clock("Same")
        with pytest.raises(ValueError, match="already exists"):
            store.create_clock("Same")

    def test_duplicate_name_update(self, store):
        store.create_clock("First")
        c2 = store.create_clock("Second")
        with pytest.raises(ValueError, match="already exists"):
            store.update_clock(c2.id, name="First")

    def test_rename_to_own_name_ok(self, store):
        c = store.create_clock("Self")
        updated = store.update_clock(c.id, name="Self", speed=9.0)
        assert updated.speed == 9.0


# ---------------------------------------------------------------------------
# Persistence
# ---------------------------------------------------------------------------


class TestSyncClockPersistence:
    def test_persist_and_reload(self, tmp_path):
        from wled_controller.storage.database import Database
        db = Database(tmp_path / "sc_persist.db")
        s1 = SyncClockStore(db)
        c = s1.create_clock(name="Persist", speed=2.5)
        cid = c.id

        s2 = SyncClockStore(db)
        loaded = s2.get_clock(cid)
        assert loaded.name == "Persist"
        assert loaded.speed == 2.5
        db.close()
