"""Tests for ValueSourceStore — CRUD for all source types."""

import pytest

from wled_controller.storage.value_source import (
    AdaptiveValueSource,
    AnimatedValueSource,
    AudioValueSource,
    DaylightValueSource,
    StaticValueSource,
    ValueSource,
)
from wled_controller.storage.value_source_store import ValueSourceStore


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


# ---------------------------------------------------------------------------
# ValueSource model round-trips
# ---------------------------------------------------------------------------


class TestValueSourceModels:
    def test_static_round_trip(self):
        data = {
            "id": "vs_1",
            "name": "Static",
            "source_type": "static",
            "value": 0.75,
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, StaticValueSource)
        assert src.value == 0.75

        restored = ValueSource.from_dict(src.to_dict())
        assert restored.value == 0.75

    def test_animated_round_trip(self):
        data = {
            "id": "vs_2",
            "name": "Wave",
            "source_type": "animated",
            "waveform": "triangle",
            "speed": 30.0,
            "min_value": 0.2,
            "max_value": 0.8,
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, AnimatedValueSource)
        assert src.waveform == "triangle"
        assert src.speed == 30.0

    def test_audio_round_trip(self):
        data = {
            "id": "vs_3",
            "name": "Audio",
            "source_type": "audio",
            "audio_source_id": "as_1",
            "mode": "peak",
            "sensitivity": 2.0,
            "smoothing": 0.5,
            "auto_gain": True,
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, AudioValueSource)
        assert src.mode == "peak"
        assert src.auto_gain is True

    def test_adaptive_time_round_trip(self):
        data = {
            "id": "vs_4",
            "name": "Time",
            "source_type": "adaptive_time",
            "schedule": [
                {"time": "00:00", "value": 0.1},
                {"time": "12:00", "value": 1.0},
            ],
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, AdaptiveValueSource)
        assert len(src.schedule) == 2

    def test_adaptive_scene_round_trip(self):
        data = {
            "id": "vs_5",
            "name": "Scene",
            "source_type": "adaptive_scene",
            "picture_source_id": "ps_1",
            "scene_behavior": "match",
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, AdaptiveValueSource)
        assert src.scene_behavior == "match"

    def test_daylight_round_trip(self):
        data = {
            "id": "vs_6",
            "name": "Daylight",
            "source_type": "daylight",
            "speed": 2.0,
            "use_real_time": True,
            "latitude": 55.0,
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, DaylightValueSource)
        assert src.use_real_time is True
        assert src.latitude == 55.0

    def test_unknown_type_defaults_to_static(self):
        data = {
            "id": "vs_u",
            "name": "Unknown",
            "source_type": "unknown_future",
            "value": 0.5,
            "created_at": "2025-01-01T00:00:00+00:00",
            "updated_at": "2025-01-01T00:00:00+00:00",
        }
        src = ValueSource.from_dict(data)
        assert isinstance(src, StaticValueSource)


# ---------------------------------------------------------------------------
# ValueSourceStore CRUD
# ---------------------------------------------------------------------------


class TestValueSourceStoreCRUD:
    def test_create_static(self, store):
        s = store.create_source(name="S1", source_type="static", value=0.5)
        assert s.id.startswith("vs_")
        assert isinstance(s, StaticValueSource)
        assert s.value == 0.5
        assert store.count() == 1

    def test_create_animated(self, store):
        s = store.create_source(
            name="A1", source_type="animated",
            waveform="sawtooth", speed=20.0,
        )
        assert isinstance(s, AnimatedValueSource)
        assert s.waveform == "sawtooth"
        assert s.speed == 20.0

    def test_create_audio(self, store):
        s = store.create_source(
            name="Au1", source_type="audio",
            audio_source_id="as_1", mode="beat",
        )
        assert isinstance(s, AudioValueSource)
        assert s.mode == "beat"

    def test_create_adaptive_time(self, store):
        schedule = [
            {"time": "08:00", "value": 0.5},
            {"time": "20:00", "value": 1.0},
        ]
        s = store.create_source(
            name="AT", source_type="adaptive_time", schedule=schedule,
        )
        assert isinstance(s, AdaptiveValueSource)
        assert len(s.schedule) == 2

    def test_create_adaptive_time_insufficient_schedule(self, store):
        with pytest.raises(ValueError, match="at least 2 points"):
            store.create_source(
                name="Bad", source_type="adaptive_time",
                schedule=[{"time": "12:00", "value": 0.5}],
            )

    def test_create_daylight(self, store):
        s = store.create_source(
            name="DL", source_type="daylight",
            speed=2.0, use_real_time=True, latitude=48.0,
        )
        assert isinstance(s, DaylightValueSource)
        assert s.use_real_time is True

    def test_create_invalid_type(self, store):
        with pytest.raises(ValueError, match="Invalid source type"):
            store.create_source(name="Bad", source_type="invalid")

    def test_get_all(self, store):
        store.create_source("A", "static")
        store.create_source("B", "static")
        assert len(store.get_all_sources()) == 2

    def test_get(self, store):
        created = store.create_source("Get", "static", value=0.3)
        got = store.get_source(created.id)
        assert got.name == "Get"

    def test_delete(self, store):
        s = store.create_source("Del", "static")
        store.delete_source(s.id)
        assert store.count() == 0

    def test_update_static(self, store):
        s = store.create_source("Stat", "static", value=0.5)
        updated = store.update_source(s.id, value=0.9)
        assert isinstance(updated, StaticValueSource)
        assert updated.value == 0.9

    def test_update_name(self, store):
        s = store.create_source("Old", "static")
        updated = store.update_source(s.id, name="New")
        assert updated.name == "New"

    def test_update_animated_fields(self, store):
        s = store.create_source("Anim", "animated", waveform="sine", speed=10.0)
        updated = store.update_source(s.id, waveform="square", speed=30.0)
        assert isinstance(updated, AnimatedValueSource)
        assert updated.waveform == "square"
        assert updated.speed == 30.0


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


class TestValueSourceNameUniqueness:
    def test_duplicate_name(self, store):
        store.create_source("Same", "static")
        with pytest.raises(ValueError, match="already exists"):
            store.create_source("Same", "animated")


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


class TestValueSourcePersistence:
    def test_persist_and_reload(self, tmp_path):
        from wled_controller.storage.database import Database
        db = Database(tmp_path / "vs_persist.db")
        s1 = ValueSourceStore(db)
        src = s1.create_source("Persist", "static", value=0.42)
        sid = src.id

        s2 = ValueSourceStore(db)
        loaded = s2.get_source(sid)
        assert loaded.name == "Persist"
        assert isinstance(loaded, StaticValueSource)
        assert loaded.value == 0.42
        db.close()
