"""BleBox climate entities tests."""

import logging
from unittest.mock import AsyncMock, PropertyMock

import blebox_uniapi
import pytest

from homeassistant.components.climate.const import (
    ATTR_CURRENT_TEMPERATURE,
    ATTR_HVAC_ACTION,
    ATTR_HVAC_MODE,
    ATTR_HVAC_MODES,
    ATTR_MAX_TEMP,
    ATTR_MIN_TEMP,
    CURRENT_HVAC_HEAT,
    CURRENT_HVAC_IDLE,
    CURRENT_HVAC_OFF,
    HVAC_MODE_HEAT,
    HVAC_MODE_OFF,
    SERVICE_SET_HVAC_MODE,
    SERVICE_SET_TEMPERATURE,
    SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import (
    ATTR_DEVICE_CLASS,
    ATTR_SUPPORTED_FEATURES,
    ATTR_TEMPERATURE,
    STATE_UNKNOWN,
)

from .conftest import async_setup_entity, mock_feature


@pytest.fixture(name="saunabox")
def saunabox_fixture():
    """Return a default climate entity mock."""
    feature = mock_feature(
        "climates",
        blebox_uniapi.climate.Climate,
        unique_id="BleBox-saunaBox-1afe34db9437-thermostat",
        full_name="saunaBox-thermostat",
        device_class=None,
        is_on=None,
        desired=None,
        current=None,
        min_temp=-54.3,
        max_temp=124.3,
    )
    product = feature.product
    type(product).name = PropertyMock(return_value="My sauna")
    type(product).model = PropertyMock(return_value="saunaBox")
    return (feature, "climate.saunabox_thermostat")


async def test_init(saunabox, hass, config):
    """Test default state."""

    _, entity_id = saunabox
    entry = await async_setup_entity(hass, config, entity_id)
    assert entry.unique_id == "BleBox-saunaBox-1afe34db9437-thermostat"

    state = hass.states.get(entity_id)
    assert state.name == "saunaBox-thermostat"

    supported_features = state.attributes[ATTR_SUPPORTED_FEATURES]
    assert supported_features & SUPPORT_TARGET_TEMPERATURE

    assert state.attributes[ATTR_HVAC_MODES] == [HVAC_MODE_OFF, HVAC_MODE_HEAT]

    assert ATTR_DEVICE_CLASS not in state.attributes
    assert ATTR_HVAC_MODE not in state.attributes
    assert ATTR_HVAC_ACTION not in state.attributes

    assert state.attributes[ATTR_MIN_TEMP] == -54.3
    assert state.attributes[ATTR_MAX_TEMP] == 124.3
    assert state.attributes[ATTR_TEMPERATURE] is None
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None

    assert state.state == STATE_UNKNOWN

    device_registry = await hass.helpers.device_registry.async_get_registry()
    device = device_registry.async_get(entry.device_id)

    assert device.name == "My sauna"
    assert device.identifiers == {("blebox", "abcd0123ef5678")}
    assert device.manufacturer == "BleBox"
    assert device.model == "saunaBox"
    assert device.sw_version == "1.23"


async def test_update(saunabox, hass, config):
    """Test updating."""

    feature_mock, entity_id = saunabox

    def initial_update():
        feature_mock.is_on = False
        feature_mock.desired = 64.3
        feature_mock.current = 40.9

    feature_mock.async_update = AsyncMock(side_effect=initial_update)
    await async_setup_entity(hass, config, entity_id)

    state = hass.states.get(entity_id)
    assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
    assert state.attributes[ATTR_TEMPERATURE] == 64.3
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 40.9
    assert state.state == HVAC_MODE_OFF


async def test_on_when_below_desired(saunabox, hass, config):
    """Test when temperature is below desired."""

    feature_mock, entity_id = saunabox

    def initial_update():
        feature_mock.is_on = False

    feature_mock.async_update = AsyncMock(side_effect=initial_update)
    await async_setup_entity(hass, config, entity_id)
    feature_mock.async_update = AsyncMock()

    def turn_on():
        feature_mock.is_on = True
        feature_mock.is_heating = True
        feature_mock.desired = 64.8
        feature_mock.current = 25.7

    feature_mock.async_on = AsyncMock(side_effect=turn_on)
    await hass.services.async_call(
        "climate",
        SERVICE_SET_HVAC_MODE,
        {"entity_id": entity_id, ATTR_HVAC_MODE: HVAC_MODE_HEAT},
        blocking=True,
    )
    feature_mock.async_off.assert_not_called()
    state = hass.states.get(entity_id)

    assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
    assert state.attributes[ATTR_TEMPERATURE] == 64.8
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 25.7
    assert state.state == HVAC_MODE_HEAT


async def test_on_when_above_desired(saunabox, hass, config):
    """Test when temperature is below desired."""

    feature_mock, entity_id = saunabox

    def initial_update():
        feature_mock.is_on = False

    feature_mock.async_update = AsyncMock(side_effect=initial_update)
    await async_setup_entity(hass, config, entity_id)
    feature_mock.async_update = AsyncMock()

    def turn_on():
        feature_mock.is_on = True
        feature_mock.is_heating = False
        feature_mock.desired = 23.4
        feature_mock.current = 28.7

    feature_mock.async_on = AsyncMock(side_effect=turn_on)

    await hass.services.async_call(
        "climate",
        SERVICE_SET_HVAC_MODE,
        {"entity_id": entity_id, ATTR_HVAC_MODE: HVAC_MODE_HEAT},
        blocking=True,
    )
    feature_mock.async_off.assert_not_called()
    state = hass.states.get(entity_id)

    assert state.attributes[ATTR_TEMPERATURE] == 23.4
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 28.7
    assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
    assert state.state == HVAC_MODE_HEAT


async def test_off(saunabox, hass, config):
    """Test turning off."""

    feature_mock, entity_id = saunabox

    def initial_update():
        feature_mock.is_on = True
        feature_mock.is_heating = False

    feature_mock.async_update = AsyncMock(side_effect=initial_update)
    await async_setup_entity(hass, config, entity_id)
    feature_mock.async_update = AsyncMock()

    def turn_off():
        feature_mock.is_on = False
        feature_mock.is_heating = False
        feature_mock.desired = 29.8
        feature_mock.current = 22.7

    feature_mock.async_off = AsyncMock(side_effect=turn_off)
    await hass.services.async_call(
        "climate",
        SERVICE_SET_HVAC_MODE,
        {"entity_id": entity_id, ATTR_HVAC_MODE: HVAC_MODE_OFF},
        blocking=True,
    )
    feature_mock.async_on.assert_not_called()
    state = hass.states.get(entity_id)

    assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
    assert state.attributes[ATTR_TEMPERATURE] == 29.8
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.7
    assert state.state == HVAC_MODE_OFF


async def test_set_thermo(saunabox, hass, config):
    """Test setting thermostat."""

    feature_mock, entity_id = saunabox

    def update():
        feature_mock.is_on = False
        feature_mock.is_heating = False

    feature_mock.async_update = AsyncMock(side_effect=update)
    await async_setup_entity(hass, config, entity_id)
    feature_mock.async_update = AsyncMock()

    def set_temp(temp):
        feature_mock.is_on = True
        feature_mock.is_heating = True
        feature_mock.desired = 29.2
        feature_mock.current = 29.1

    feature_mock.async_set_temperature = AsyncMock(side_effect=set_temp)
    await hass.services.async_call(
        "climate",
        SERVICE_SET_TEMPERATURE,
        {"entity_id": entity_id, ATTR_TEMPERATURE: 43.21},
        blocking=True,
    )
    state = hass.states.get(entity_id)

    assert state.attributes[ATTR_TEMPERATURE] == 29.2
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 29.1
    assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
    assert state.state == HVAC_MODE_HEAT


async def test_update_failure(saunabox, hass, config, caplog):
    """Test that update failures are logged."""

    caplog.set_level(logging.ERROR)

    feature_mock, entity_id = saunabox
    feature_mock.async_update = AsyncMock(side_effect=blebox_uniapi.error.ClientError)
    await async_setup_entity(hass, config, entity_id)

    assert f"Updating '{feature_mock.full_name}' failed: " in caplog.text