"""Test the Dyson fan component."""
from __future__ import annotations

from libpurecool.const import (
    AutoMode,
    FanPower,
    FanSpeed,
    FanState,
    FocusMode,
    HeatMode,
    HeatState,
)
from libpurecool.dyson_device import DysonDevice
from libpurecool.dyson_pure_hotcool import DysonPureHotCool
from libpurecool.dyson_pure_hotcool_link import DysonPureHotCoolLink
from libpurecool.dyson_pure_state import DysonPureHotCoolState
from libpurecool.dyson_pure_state_v2 import DysonPureHotCoolV2State
import pytest

from homeassistant.components.climate import DOMAIN as PLATFORM_DOMAIN
from homeassistant.components.climate.const import (
    ATTR_CURRENT_HUMIDITY,
    ATTR_CURRENT_TEMPERATURE,
    ATTR_FAN_MODE,
    ATTR_FAN_MODES,
    ATTR_HVAC_ACTION,
    ATTR_HVAC_MODE,
    ATTR_HVAC_MODES,
    ATTR_MAX_TEMP,
    ATTR_MIN_TEMP,
    CURRENT_HVAC_COOL,
    CURRENT_HVAC_HEAT,
    CURRENT_HVAC_IDLE,
    CURRENT_HVAC_OFF,
    FAN_AUTO,
    FAN_DIFFUSE,
    FAN_FOCUS,
    FAN_HIGH,
    FAN_LOW,
    FAN_MEDIUM,
    FAN_OFF,
    HVAC_MODE_COOL,
    HVAC_MODE_HEAT,
    HVAC_MODE_OFF,
    SERVICE_SET_FAN_MODE,
    SERVICE_SET_HVAC_MODE,
    SERVICE_SET_TEMPERATURE,
)
from homeassistant.components.dyson.climate import (
    SUPPORT_FAN,
    SUPPORT_FAN_PCOOL,
    SUPPORT_FLAGS,
    SUPPORT_HVAC,
    SUPPORT_HVAC_PCOOL,
)
from homeassistant.const import (
    ATTR_ENTITY_ID,
    ATTR_SUPPORTED_FEATURES,
    ATTR_TEMPERATURE,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er

from .common import (
    ENTITY_NAME,
    NAME,
    SERIAL,
    async_get_basic_device,
    async_update_device,
)

ENTITY_ID = f"{PLATFORM_DOMAIN}.{ENTITY_NAME}"


@callback
def async_get_device(spec: type[DysonDevice]) -> DysonDevice:
    """Return a Dyson climate device."""
    device = async_get_basic_device(spec)
    device.state.heat_target = 2900
    device.environmental_state.temperature = 275
    device.environmental_state.humidity = 50
    if spec == DysonPureHotCoolLink:
        device.state.heat_mode = HeatMode.HEAT_ON.value
        device.state.heat_state = HeatState.HEAT_STATE_ON.value
        device.state.focus_mode = FocusMode.FOCUS_ON.value
    else:
        device.state.fan_power = FanPower.POWER_ON.value
        device.state.heat_mode = HeatMode.HEAT_ON.value
        device.state.heat_state = HeatState.HEAT_STATE_ON.value
        device.state.auto_mode = AutoMode.AUTO_ON.value
        device.state.fan_state = FanState.FAN_OFF.value
        device.state.speed = FanSpeed.FAN_SPEED_AUTO.value
    return device


@pytest.mark.parametrize(
    "device", [DysonPureHotCoolLink, DysonPureHotCool], indirect=True
)
async def test_state_common(hass: HomeAssistant, device: DysonDevice) -> None:
    """Test common state and attributes of two types of climate entities."""
    entity_registry = er.async_get(hass)
    assert entity_registry.async_get(ENTITY_ID).unique_id == SERIAL

    state = hass.states.get(ENTITY_ID)
    assert state.name == NAME
    attributes = state.attributes
    assert attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_FLAGS
    assert attributes[ATTR_CURRENT_TEMPERATURE] == 2
    assert attributes[ATTR_CURRENT_HUMIDITY] == 50
    assert attributes[ATTR_TEMPERATURE] == 17
    assert attributes[ATTR_MIN_TEMP] == 1
    assert attributes[ATTR_MAX_TEMP] == 37

    device.state.heat_target = 2800
    device.environmental_state.temperature = 0
    device.environmental_state.humidity = 0
    await async_update_device(
        hass,
        device,
        DysonPureHotCoolState
        if isinstance(device, DysonPureHotCoolLink)
        else DysonPureHotCoolV2State,
    )
    attributes = hass.states.get(ENTITY_ID).attributes
    assert attributes[ATTR_CURRENT_TEMPERATURE] is None
    assert ATTR_CURRENT_HUMIDITY not in attributes
    assert attributes[ATTR_TEMPERATURE] == 7


@pytest.mark.parametrize("device", [DysonPureHotCoolLink], indirect=True)
async def test_state_purehotcoollink(
    hass: HomeAssistant, device: DysonPureHotCoolLink
) -> None:
    """Test common state and attributes of a PureHotCoolLink entity."""
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_HEAT
    attributes = state.attributes
    assert attributes[ATTR_HVAC_MODES] == SUPPORT_HVAC
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
    assert attributes[ATTR_FAN_MODE] == FAN_FOCUS
    assert attributes[ATTR_FAN_MODES] == SUPPORT_FAN

    device.state.heat_state = HeatState.HEAT_STATE_OFF.value
    device.state.focus_mode = FocusMode.FOCUS_OFF
    await async_update_device(hass, device, DysonPureHotCoolState)
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_HEAT
    attributes = state.attributes
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
    assert attributes[ATTR_FAN_MODE] == FAN_DIFFUSE

    device.state.heat_mode = HeatMode.HEAT_OFF.value
    await async_update_device(hass, device, DysonPureHotCoolState)
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_COOL
    attributes = state.attributes
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL


@pytest.mark.parametrize("device", [DysonPureHotCool], indirect=True)
async def test_state_purehotcool(hass: HomeAssistant, device: DysonPureHotCool) -> None:
    """Test common state and attributes of a PureHotCool entity."""
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_HEAT
    attributes = state.attributes
    assert attributes[ATTR_HVAC_MODES] == SUPPORT_HVAC_PCOOL
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
    assert attributes[ATTR_FAN_MODE] == FAN_AUTO
    assert attributes[ATTR_FAN_MODES] == SUPPORT_FAN_PCOOL

    device.state.heat_state = HeatState.HEAT_STATE_OFF.value
    device.state.auto_mode = AutoMode.AUTO_OFF.value
    await async_update_device(hass, device, DysonPureHotCoolV2State)
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_HEAT
    attributes = state.attributes
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
    assert attributes[ATTR_FAN_MODE] == FAN_OFF

    device.state.heat_mode = HeatMode.HEAT_OFF.value
    device.state.fan_state = FanState.FAN_ON.value
    device.state.speed = FanSpeed.FAN_SPEED_1.value
    await async_update_device(hass, device, DysonPureHotCoolV2State)
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_COOL
    attributes = state.attributes
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
    assert attributes[ATTR_FAN_MODE] == FAN_LOW

    device.state.fan_power = FanPower.POWER_OFF.value
    await async_update_device(hass, device, DysonPureHotCoolV2State)
    state = hass.states.get(ENTITY_ID)
    assert state.state == HVAC_MODE_OFF
    attributes = state.attributes
    assert attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF


@pytest.mark.parametrize(
    "service,service_data,configuration_data",
    [
        (
            SERVICE_SET_TEMPERATURE,
            {ATTR_TEMPERATURE: -5},
            {"heat_target": "2740", "heat_mode": HeatMode.HEAT_ON},
        ),
        (
            SERVICE_SET_TEMPERATURE,
            {ATTR_TEMPERATURE: 40},
            {"heat_target": "3100", "heat_mode": HeatMode.HEAT_ON},
        ),
        (
            SERVICE_SET_TEMPERATURE,
            {ATTR_TEMPERATURE: 20},
            {"heat_target": "2930", "heat_mode": HeatMode.HEAT_ON},
        ),
        (
            SERVICE_SET_FAN_MODE,
            {ATTR_FAN_MODE: FAN_FOCUS},
            {"focus_mode": FocusMode.FOCUS_ON},
        ),
        (
            SERVICE_SET_FAN_MODE,
            {ATTR_FAN_MODE: FAN_DIFFUSE},
            {"focus_mode": FocusMode.FOCUS_OFF},
        ),
        (
            SERVICE_SET_HVAC_MODE,
            {ATTR_HVAC_MODE: HVAC_MODE_HEAT},
            {"heat_mode": HeatMode.HEAT_ON},
        ),
        (
            SERVICE_SET_HVAC_MODE,
            {ATTR_HVAC_MODE: HVAC_MODE_COOL},
            {"heat_mode": HeatMode.HEAT_OFF},
        ),
    ],
)
@pytest.mark.parametrize("device", [DysonPureHotCoolLink], indirect=True)
async def test_commands_purehotcoollink(
    hass: HomeAssistant,
    device: DysonPureHotCoolLink,
    service: str,
    service_data: dict,
    configuration_data: dict,
) -> None:
    """Test sending commands to a PureHotCoolLink entity."""
    await hass.services.async_call(
        PLATFORM_DOMAIN,
        service,
        {
            ATTR_ENTITY_ID: ENTITY_ID,
            **service_data,
        },
        blocking=True,
    )
    device.set_configuration.assert_called_once_with(**configuration_data)


@pytest.mark.parametrize(
    "service,service_data,command,command_args",
    [
        (SERVICE_SET_TEMPERATURE, {ATTR_TEMPERATURE: 20}, "set_heat_target", ["2930"]),
        (SERVICE_SET_FAN_MODE, {ATTR_FAN_MODE: FAN_OFF}, "turn_off", []),
        (
            SERVICE_SET_FAN_MODE,
            {ATTR_FAN_MODE: FAN_LOW},
            "set_fan_speed",
            [FanSpeed.FAN_SPEED_4],
        ),
        (
            SERVICE_SET_FAN_MODE,
            {ATTR_FAN_MODE: FAN_MEDIUM},
            "set_fan_speed",
            [FanSpeed.FAN_SPEED_7],
        ),
        (
            SERVICE_SET_FAN_MODE,
            {ATTR_FAN_MODE: FAN_HIGH},
            "set_fan_speed",
            [FanSpeed.FAN_SPEED_10],
        ),
        (SERVICE_SET_FAN_MODE, {ATTR_FAN_MODE: FAN_AUTO}, "enable_auto_mode", []),
        (SERVICE_SET_HVAC_MODE, {ATTR_HVAC_MODE: HVAC_MODE_OFF}, "turn_off", []),
        (
            SERVICE_SET_HVAC_MODE,
            {ATTR_HVAC_MODE: HVAC_MODE_HEAT},
            "enable_heat_mode",
            [],
        ),
        (
            SERVICE_SET_HVAC_MODE,
            {ATTR_HVAC_MODE: HVAC_MODE_COOL},
            "disable_heat_mode",
            [],
        ),
    ],
)
@pytest.mark.parametrize("device", [DysonPureHotCool], indirect=True)
async def test_commands_purehotcool(
    hass: HomeAssistant,
    device: DysonPureHotCoolLink,
    service: str,
    service_data: dict,
    command: str,
    command_args: list,
) -> None:
    """Test sending commands to a PureHotCool entity."""
    await hass.services.async_call(
        PLATFORM_DOMAIN,
        service,
        {
            ATTR_ENTITY_ID: ENTITY_ID,
            **service_data,
        },
        blocking=True,
    )
    getattr(device, command).assert_called_once_with(*command_args)


@pytest.mark.parametrize("hvac_mode", [HVAC_MODE_HEAT, HVAC_MODE_COOL])
@pytest.mark.parametrize(
    "fan_power,turn_on_call_count",
    [
        (FanPower.POWER_ON.value, 0),
        (FanPower.POWER_OFF.value, 1),
    ],
)
@pytest.mark.parametrize("device", [DysonPureHotCool], indirect=True)
async def test_set_hvac_mode_purehotcool(
    hass: HomeAssistant,
    device: DysonPureHotCoolLink,
    hvac_mode: str,
    fan_power: str,
    turn_on_call_count: int,
) -> None:
    """Test setting HVAC mode of a PureHotCool entity turns on the device when it's off."""
    device.state.fan_power = fan_power
    await async_update_device(hass, device)
    await hass.services.async_call(
        PLATFORM_DOMAIN,
        SERVICE_SET_HVAC_MODE,
        {
            ATTR_ENTITY_ID: ENTITY_ID,
            ATTR_HVAC_MODE: hvac_mode,
        },
        blocking=True,
    )
    assert device.turn_on.call_count == turn_on_call_count