from __future__ import annotations

import pytest
from pytest_mock import MockerFixture

from kasa import Module
from kasa.smart import SmartDevice

from ...device_fixtures import get_parent_and_child_modules, parametrize

speaker = parametrize(
    "has speaker", component_filter="speaker", protocol_filter={"SMART"}
)


@speaker
@pytest.mark.parametrize(
    ("feature", "prop_name", "type"),
    [
        ("volume", "volume", int),
    ],
)
async def test_features(dev: SmartDevice, feature: str, prop_name: str, type: type):
    """Test that features are registered and work as expected."""
    speaker = next(get_parent_and_child_modules(dev, Module.Speaker))
    assert speaker is not None

    prop = getattr(speaker, prop_name)
    assert isinstance(prop, type)

    feat = speaker._device.features[feature]
    assert feat.value == prop
    assert isinstance(feat.value, type)


@speaker
async def test_set_volume(dev: SmartDevice, mocker: MockerFixture):
    """Test speaker settings."""
    speaker = next(get_parent_and_child_modules(dev, Module.Speaker))
    assert speaker is not None

    call = mocker.spy(speaker, "call")

    volume = speaker._device.features["volume"]
    assert speaker.volume == volume.value

    new_volume = 15
    await speaker.set_volume(new_volume)

    call.assert_called_with("setVolume", {"volume": new_volume})

    await dev.update()

    assert speaker.volume == new_volume

    with pytest.raises(ValueError, match="Volume must be between 0 and 100"):
        await speaker.set_volume(-10)

    with pytest.raises(ValueError, match="Volume must be between 0 and 100"):
        await speaker.set_volume(110)


@speaker
async def test_locate(dev: SmartDevice, mocker: MockerFixture):
    """Test the locate method."""
    speaker = next(get_parent_and_child_modules(dev, Module.Speaker))
    call = mocker.spy(speaker, "call")

    await speaker.locate()

    call.assert_called_with("playSelectAudio", {"audio_type": "seek_me"})