from __future__ import annotations

from itertools import chain

import pytest
from pytest_mock import MockerFixture

from kasa import Device, Feature, Module
from kasa.smart.modules import LightEffect

from ...device_fixtures import parametrize

light_effect = parametrize(
    "has light effect", component_filter="light_effect", protocol_filter={"SMART"}
)


@light_effect
async def test_light_effect(dev: Device, mocker: MockerFixture):
    """Test light effect."""
    light_effect = dev.modules.get(Module.LightEffect)
    assert isinstance(light_effect, LightEffect)

    feature = dev.features["light_effect"]
    assert feature.type == Feature.Type.Choice

    call = mocker.spy(light_effect, "call")
    assert feature.choices == light_effect.effect_list
    assert feature.choices
    for effect in chain(reversed(feature.choices), feature.choices):
        await light_effect.set_effect(effect)
        enable = effect != LightEffect.LIGHT_EFFECTS_OFF
        params: dict[str, bool | str] = {"enable": enable}
        if enable:
            params["id"] = light_effect._scenes_names_to_id[effect]
        call.assert_called_with("set_dynamic_light_effect_rule_enable", params)
        await dev.update()
        assert light_effect.effect == effect
        assert feature.value == effect

    with pytest.raises(ValueError, match="The effect foobar is not a built in effect"):
        await light_effect.set_effect("foobar")


@light_effect
@pytest.mark.parametrize("effect_active", [True, False])
async def test_light_effect_brightness(
    dev: Device, effect_active: bool, mocker: MockerFixture
):
    """Test that light module uses light_effect for brightness when active."""
    light_module = dev.modules[Module.Light]

    light_effect = dev.modules[Module.SmartLightEffect]
    light_effect_set_brightness = mocker.spy(light_effect, "set_brightness")
    mock_light_effect_call = mocker.patch.object(light_effect, "call")

    brightness = dev.modules[Module.Brightness]
    brightness_set_brightness = mocker.spy(brightness, "set_brightness")
    mock_brightness_call = mocker.patch.object(brightness, "call")

    mocker.patch.object(
        type(light_effect),
        "is_active",
        new_callable=mocker.PropertyMock,
        return_value=effect_active,
    )
    if effect_active:  # Set the rule L1 active for testing
        light_effect.data["current_rule_id"] = "L1"

    await light_module.set_brightness(10)

    if effect_active:
        assert light_effect.is_active
        assert light_effect.brightness == light_module.brightness

        light_effect_set_brightness.assert_called_with(10)
        mock_light_effect_call.assert_called_with(
            "edit_dynamic_light_effect_rule", mocker.ANY
        )
    else:
        assert not light_effect.is_active

        brightness_set_brightness.assert_called_with(10)
        mock_brightness_call.assert_called_with("set_device_info", {"brightness": 10})
