"""Tests of the climate entity of the balboa integration."""
from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest

from homeassistant.components.climate.const import (
    ATTR_FAN_MODE,
    ATTR_HVAC_ACTION,
    ATTR_HVAC_MODES,
    ATTR_MAX_TEMP,
    ATTR_MIN_TEMP,
    ATTR_PRESET_MODE,
    ATTR_PRESET_MODES,
    FAN_HIGH,
    FAN_LOW,
    FAN_MEDIUM,
    FAN_OFF,
    ClimateEntityFeature,
    HVACAction,
    HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_FAHRENHEIT
from homeassistant.core import HomeAssistant

from . import init_integration

from tests.components.climate import common

FAN_SETTINGS = [
    FAN_OFF,
    FAN_LOW,
    FAN_MEDIUM,
    FAN_HIGH,
]

HVAC_SETTINGS = [
    HVACMode.HEAT,
    HVACMode.OFF,
    HVACMode.AUTO,
]

ENTITY_CLIMATE = "climate.fakespa_climate"


async def test_spa_defaults(hass: HomeAssistant, client: MagicMock) -> None:
    """Test supported features flags."""
    await init_integration(hass)

    state = hass.states.get(ENTITY_CLIMATE)

    assert state
    assert (
        state.attributes["supported_features"]
        == ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
    )
    assert state.state == HVACMode.HEAT
    assert state.attributes[ATTR_MIN_TEMP] == 10.0
    assert state.attributes[ATTR_MAX_TEMP] == 40.0
    assert state.attributes[ATTR_PRESET_MODE] == "Ready"
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE


async def test_spa_defaults_fake_tscale(hass: HomeAssistant, client: MagicMock) -> None:
    """Test supported features flags."""
    client.get_tempscale.return_value = 1

    await init_integration(hass)

    state = hass.states.get(ENTITY_CLIMATE)

    assert state
    assert (
        state.attributes["supported_features"]
        == ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
    )
    assert state.state == HVACMode.HEAT
    assert state.attributes[ATTR_MIN_TEMP] == 10.0
    assert state.attributes[ATTR_MAX_TEMP] == 40.0
    assert state.attributes[ATTR_PRESET_MODE] == "Ready"
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE


async def test_spa_with_blower(hass: HomeAssistant, client: MagicMock) -> None:
    """Test supported features flags."""
    client.have_blower.return_value = True

    config_entry = await init_integration(hass)

    # force a refresh
    await client.new_data_cb()
    await hass.async_block_till_done()

    state = hass.states.get(ENTITY_CLIMATE)

    assert state
    assert (
        state.attributes["supported_features"]
        == ClimateEntityFeature.TARGET_TEMPERATURE
        | ClimateEntityFeature.PRESET_MODE
        | ClimateEntityFeature.FAN_MODE
    )

    for fan_state in range(4):
        # set blower
        state = await _patch_blower(hass, config_entry, fan_state, client)
        assert state
        assert state.attributes[ATTR_FAN_MODE] == FAN_SETTINGS[fan_state]

    # test the nonsense checks
    for fan_state in (None, 70):  # type: ignore[assignment]
        state = await _patch_blower(hass, config_entry, fan_state, client)
        assert state
        assert state.attributes[ATTR_FAN_MODE] == FAN_OFF


async def test_spa_temperature(hass: HomeAssistant, client: MagicMock) -> None:
    """Test spa temperature settings."""

    config_entry = await init_integration(hass)

    # flip the spa into F
    # set temp to a valid number
    state = await _patch_spa_settemp(hass, config_entry, 0, 100.0, client)
    assert state
    assert state.attributes.get(ATTR_TEMPERATURE) == 38.0


async def test_spa_temperature_unit(hass: HomeAssistant, client: MagicMock) -> None:
    """Test temperature unit conversions."""

    with patch.object(hass.config.units, "temperature_unit", TEMP_FAHRENHEIT):
        config_entry = await init_integration(hass)

        state = await _patch_spa_settemp(hass, config_entry, 0, 15.4, client)
        assert state
        assert state.attributes.get(ATTR_TEMPERATURE) == 15.0


async def test_spa_hvac_modes(hass: HomeAssistant, client: MagicMock) -> None:
    """Test hvac modes."""

    config_entry = await init_integration(hass)

    # try out the different heat modes
    for heat_mode in range(2):
        state = await _patch_spa_heatmode(hass, config_entry, heat_mode, client)
        assert state
        modes = state.attributes.get(ATTR_HVAC_MODES)
        assert [HVACMode.AUTO, HVACMode.HEAT, HVACMode.OFF] == modes
        assert state.state == HVAC_SETTINGS[heat_mode]

    with pytest.raises(ValueError):
        await _patch_spa_heatmode(hass, config_entry, 2, client)


async def test_spa_hvac_action(hass: HomeAssistant, client: MagicMock) -> None:
    """Test setting of the HVAC action."""

    config_entry = await init_integration(hass)

    # try out the different heat states
    state = await _patch_spa_heatstate(hass, config_entry, 1, client)
    assert state
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING

    state = await _patch_spa_heatstate(hass, config_entry, 0, client)
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE


async def test_spa_preset_modes(hass: HomeAssistant, client: MagicMock) -> None:
    """Test the various preset modes."""

    await init_integration(hass)

    state = hass.states.get(ENTITY_CLIMATE)
    assert state
    modes = state.attributes.get(ATTR_PRESET_MODES)
    assert ["Ready", "Rest", "Ready in Rest"] == modes

    # Put it in Ready and Rest
    modelist = ["Ready", "Rest"]
    for mode in modelist:
        client.heatmode = modelist.index(mode)
        await common.async_set_preset_mode(hass, mode, ENTITY_CLIMATE)
        await client.new_data_cb()
        await hass.async_block_till_done()

        state = hass.states.get(ENTITY_CLIMATE)
        assert state
        assert state.attributes[ATTR_PRESET_MODE] == mode

    # put it in RNR and test assertion
    client.heatmode = 2

    with pytest.raises(ValueError):
        await common.async_set_preset_mode(hass, 2, ENTITY_CLIMATE)


# Helpers
async def _patch_blower(hass, config_entry, fan_state, client):
    """Patch the blower state."""
    client.get_blower.return_value = fan_state

    if fan_state is not None and fan_state <= len(FAN_SETTINGS):
        await common.async_set_fan_mode(hass, FAN_SETTINGS[fan_state])
    await client.new_data_cb()
    await hass.async_block_till_done()

    return hass.states.get(ENTITY_CLIMATE)


async def _patch_spa_settemp(hass, config_entry, tscale, settemp, client):
    """Patch the settemp."""
    client.get_tempscale.return_value = tscale
    client.get_settemp.return_value = settemp

    await common.async_set_temperature(
        hass, temperature=settemp, entity_id=ENTITY_CLIMATE
    )
    await client.new_data_cb()
    await hass.async_block_till_done()

    return hass.states.get(ENTITY_CLIMATE)


async def _patch_spa_heatmode(hass, config_entry, heat_mode, client):
    """Patch the heatmode."""
    client.heatmode = heat_mode

    await common.async_set_hvac_mode(hass, HVAC_SETTINGS[heat_mode], ENTITY_CLIMATE)
    await client.new_data_cb()
    await hass.async_block_till_done()

    return hass.states.get(ENTITY_CLIMATE)


async def _patch_spa_heatstate(hass, config_entry, heat_state, client):
    """Patch the heatmode."""
    client.get_heatstate.return_value = heat_state

    await common.async_set_hvac_mode(hass, HVAC_SETTINGS[heat_state], ENTITY_CLIMATE)
    await client.new_data_cb()
    await hass.async_block_till_done()

    return hass.states.get(ENTITY_CLIMATE)