"""BleBox climate entities tests."""

import logging
from unittest.mock import AsyncMock, PropertyMock

import blebox_uniapi
import pytest

from homeassistant.components.climate import (
    ATTR_CURRENT_TEMPERATURE,
    ATTR_HVAC_ACTION,
    ATTR_HVAC_MODE,
    ATTR_HVAC_MODES,
    ATTR_MAX_TEMP,
    ATTR_MIN_TEMP,
    SERVICE_SET_HVAC_MODE,
    SERVICE_SET_TEMPERATURE,
    ClimateEntityFeature,
    HVACAction,
    HVACMode,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
    ATTR_DEVICE_CLASS,
    ATTR_ENTITY_ID,
    ATTR_SUPPORTED_FEATURES,
    ATTR_TEMPERATURE,
    STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr

from .conftest import async_setup_config_entry, async_setup_entity, mock_feature

from tests.common import MockConfigEntry


@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,
        mode=None,
        hvac_action=None,
    )
    product = feature.product
    type(product).name = PropertyMock(return_value="My sauna")
    type(product).model = PropertyMock(return_value="saunaBox")
    type(product).product = PropertyMock(return_value="saunaBox")
    return (feature, "climate.my_sauna")


@pytest.fixture(name="thermobox")
def thermobox_fixture():
    """Return a default climate entity mock."""
    feature = mock_feature(
        "climates",
        blebox_uniapi.climate.Climate,
        unique_id="BleBox-thermoBox-1afe34db9437-thermostat",
        full_name="thermoBox-thermostat",
        device_class=None,
        is_on=None,
        desired=None,
        current=None,
        min_temp=-54.3,
        max_temp=124.3,
        mode=2,
        hvac_action=1,
    )
    product = feature.product
    type(product).name = PropertyMock(return_value="My thermo")
    type(product).model = PropertyMock(return_value="thermoBox")
    type(product).product = PropertyMock(return_value="thermoBox")
    return (feature, "climate.my_thermo")


async def test_init(
    saunabox, hass: HomeAssistant, device_registry: dr.DeviceRegistry
) -> None:
    """Test default state."""

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

    state = hass.states.get(entity_id)
    assert state.name == "My sauna"

    supported_features = state.attributes[ATTR_SUPPORTED_FEATURES]
    assert supported_features & ClimateEntityFeature.TARGET_TEMPERATURE

    assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF]

    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 = 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: HomeAssistant, config) -> None:
    """Test updating."""

    feature_mock, entity_id = saunabox

    feature_mock.is_on = False
    feature_mock.desired = 64.3
    feature_mock.current = 40.9
    await async_setup_entity(hass, entity_id)

    state = hass.states.get(entity_id)
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
    assert state.attributes[ATTR_TEMPERATURE] == 64.3
    assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 40.9
    assert state.state == HVACMode.OFF


async def test_set_hvac_mode_heat(saunabox, hass: HomeAssistant) -> None:
    """Test that setting HVAC mode to heat calls async_on."""

    feature_mock, entity_id = saunabox

    feature_mock.mode = 1
    await async_setup_entity(hass, entity_id)

    await hass.services.async_call(
        "climate",
        SERVICE_SET_HVAC_MODE,
        {ATTR_ENTITY_ID: entity_id, ATTR_HVAC_MODE: HVACMode.HEAT},
        blocking=True,
    )

    feature_mock.async_on.assert_called_once_with()
    feature_mock.async_off.assert_not_called()


async def test_set_hvac_mode_off(saunabox, hass: HomeAssistant) -> None:
    """Test that setting HVAC mode to off calls async_off."""

    feature_mock, entity_id = saunabox

    await async_setup_entity(hass, entity_id)

    await hass.services.async_call(
        "climate",
        SERVICE_SET_HVAC_MODE,
        {"entity_id": entity_id, ATTR_HVAC_MODE: HVACMode.OFF},
        blocking=True,
    )

    feature_mock.async_off.assert_called_once_with()
    feature_mock.async_on.assert_not_called()


async def test_set_thermo(saunabox, hass: HomeAssistant) -> None:
    """Test that setting the temperature calls async_set_temperature."""

    feature_mock, entity_id = saunabox

    await async_setup_entity(hass, entity_id)

    await hass.services.async_call(
        "climate",
        SERVICE_SET_TEMPERATURE,
        {"entity_id": entity_id, ATTR_TEMPERATURE: 43.21},
        blocking=True,
    )

    feature_mock.async_set_temperature.assert_called_once_with(43.21)


async def test_update_failure(
    saunabox,
    hass: HomeAssistant,
    config_entry: MockConfigEntry,
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test that update failures cause config entry setup retry."""

    caplog.set_level(logging.ERROR)

    feature_mock, _entity_id = saunabox
    feature_mock.product.async_update_data = AsyncMock(
        side_effect=blebox_uniapi.error.ClientError
    )

    await async_setup_config_entry(hass, config_entry)

    feature_mock.product.async_update_data.assert_called()
    assert config_entry.state is ConfigEntryState.SETUP_RETRY


async def test_hvac_action_heating(saunabox, hass: HomeAssistant) -> None:
    """Test hvac_action reflects a heating device state."""

    feature_mock, entity_id = saunabox

    feature_mock.is_on = True
    feature_mock.hvac_action = 1
    feature_mock.mode = 1
    await async_setup_entity(hass, entity_id)

    state = hass.states.get(entity_id)
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.HEATING
    assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, HVACMode.HEAT]


async def test_hvac_action_off(thermobox, hass: HomeAssistant) -> None:
    """Test hvac_action reflects a device that is off."""

    feature_mock, entity_id = thermobox

    feature_mock.is_on = False
    feature_mock.hvac_action = 0
    await async_setup_entity(hass, entity_id)

    state = hass.states.get(entity_id)
    assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.OFF
    assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.OFF, HVACMode.COOL]
