from __future__ import annotations

from datetime import datetime

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

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


@autooff
@pytest.mark.parametrize(
    ("feature", "prop_name", "type"),
    [
        ("auto_off_enabled", "enabled", bool),
        ("auto_off_minutes", "delay", int),
        ("auto_off_at", "auto_off_at", datetime | None),
    ],
)
async def test_autooff_features(
    dev: SmartDevice, feature: str, prop_name: str, type: type
):
    """Test that features are registered and work as expected."""
    autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
    assert autooff is not None

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

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


@autooff
async def test_settings(dev: SmartDevice, mocker: MockerFixture):
    """Test autooff settings."""
    autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
    assert autooff

    enabled = autooff._device.features["auto_off_enabled"]
    assert autooff.enabled == enabled.value

    delay = autooff._device.features["auto_off_minutes"]
    assert autooff.delay == delay.value

    call = mocker.spy(autooff, "call")
    new_state = True

    await autooff.set_enabled(new_state)
    call.assert_called_with(
        "set_auto_off_config", {"enable": new_state, "delay_min": delay.value}
    )
    call.reset_mock()
    await dev.update()

    new_delay = 123

    await autooff.set_delay(new_delay)

    call.assert_called_with(
        "set_auto_off_config", {"enable": new_state, "delay_min": new_delay}
    )

    await dev.update()

    assert autooff.enabled == new_state
    assert autooff.delay == new_delay


@autooff
@pytest.mark.parametrize("is_timer_active", [True, False])
async def test_auto_off_at(
    dev: SmartDevice, mocker: MockerFixture, is_timer_active: bool
):
    """Test auto-off at sensor."""
    autooff = next(get_parent_and_child_modules(dev, Module.AutoOff))
    assert autooff

    autooff_at = autooff._device.features["auto_off_at"]

    mocker.patch.object(
        type(autooff),
        "is_timer_active",
        new_callable=mocker.PropertyMock,
        return_value=is_timer_active,
    )
    if is_timer_active:
        assert isinstance(autooff_at.value, datetime)
    else:
        assert autooff_at.value is None