# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long

""" Test the normal start of a Thermostat """
from unittest.mock import patch #, call
from datetime import datetime, timedelta

from homeassistant.core import HomeAssistant
from homeassistant.components.climate import HVACAction, HVACMode
from homeassistant.config_entries import ConfigEntryState

from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN

from pytest_homeassistant_custom_component.common import MockConfigEntry

# from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_climate import ThermostatOverClimate

from .commons import *  # pylint: disable=wildcard-import, unused-wildcard-import

@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
    """Test the regulation of an over climate thermostat"""

    entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="uniqueId",
        # This is include a medium regulation
        data=PARTIAL_CLIMATE_CONFIG,
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})

    # Creates the regulated VTherm over climate
    # change temperature so that the heating will start
    event_timestamp = now - timedelta(minutes=10)

    with patch(
        "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
    ), patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)
        assert entry.state is ConfigEntryState.LOADED

        def find_my_entity(entity_id) -> ClimateEntity:
            """Find my new entity"""
            component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
            for entity in component.entities:
                if entity.entity_id == entity_id:
                    return entity

        entity:ThermostatOverClimate = find_my_entity("climate.theoverclimatemockname")

        assert entity
        assert isinstance(entity, ThermostatOverClimate)

        assert entity.name == "TheOverClimateMockName"
        assert entity.is_over_climate is True
        assert entity.is_regulated is True
        assert entity.hvac_action is HVACAction.OFF
        assert entity.hvac_mode is HVACMode.OFF
        assert entity.target_temperature == entity.min_temp
        assert entity.preset_modes == [
            PRESET_NONE,
            PRESET_ECO,
            PRESET_COMFORT,
            PRESET_BOOST,
        ]
        assert entity.preset_mode is PRESET_NONE

        # Activate the heating by changing HVACMode and temperature
        # Select a hvacmode, presence and preset
        await entity.async_set_hvac_mode(HVACMode.HEAT)
        assert entity.hvac_mode is HVACMode.HEAT
        assert entity.hvac_action == HVACAction.OFF

        assert entity.regulated_target_temp == entity.min_temp

        await send_temperature_change_event(entity, 15, event_timestamp)
        await send_ext_temperature_change_event(entity, 10, event_timestamp)

        # set manual target temp (at now - 7) -> the regulation should occurs
        event_timestamp = now - timedelta(minutes=7)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await entity.async_set_temperature(temperature=18)

            fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
            assert entity.hvac_action == HVACAction.HEATING
            assert entity.preset_mode == PRESET_NONE # Manual mode

            # the regulated temperature should be greater
            assert entity.regulated_target_temp > entity.target_temperature
            # In medium we could go up to +3 degre
            # normally the calcul gives 18 + 2.2 but we round the result to the nearest 0.5 which is 2.0
            assert entity.regulated_target_temp == 18+2.0
            assert entity.hvac_action == HVACAction.HEATING

        # change temperature so that the regulated temperature should slow down
        event_timestamp = now - timedelta(minutes=5)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await send_temperature_change_event(entity, 23, event_timestamp)
            await send_ext_temperature_change_event(entity, 19, event_timestamp)

            # the regulated temperature should be under
            assert entity.regulated_target_temp < entity.target_temperature
            assert entity.regulated_target_temp == 18-0.5 # normally 0.6 but round_to_nearest gives 0.5

@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
    """Test the regulation of an over climate thermostat"""

    entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="uniqueId",
        # This is include a medium regulation
        data=PARTIAL_CLIMATE_AC_CONFIG,
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})

    # Creates the regulated VTherm over climate
    # change temperature so that the heating will start
    event_timestamp = now - timedelta(minutes=10)

    with patch(
        "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
    ), patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)
        assert entry.state is ConfigEntryState.LOADED

        def find_my_entity(entity_id) -> ClimateEntity:
            """Find my new entity"""
            component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
            for entity in component.entities:
                if entity.entity_id == entity_id:
                    return entity

        entity:ThermostatOverClimate = find_my_entity("climate.theoverclimatemockname")

        assert entity
        assert isinstance(entity, ThermostatOverClimate)

        assert entity.name == "TheOverClimateMockName"
        assert entity.is_over_climate is True
        assert entity.is_regulated is True
        assert entity.hvac_action is HVACAction.OFF
        assert entity.hvac_mode is HVACMode.OFF
        assert entity.target_temperature == entity.max_temp
        assert entity.preset_modes == [
            PRESET_NONE,
            PRESET_ECO,
            PRESET_COMFORT,
            PRESET_BOOST,
        ]
        assert entity.preset_mode is PRESET_NONE

        # Activate the heating by changing HVACMode and temperature
        # Select a hvacmode, presence and preset
        await entity.async_set_hvac_mode(HVACMode.HEAT)
        assert entity.hvac_mode is HVACMode.HEAT
        assert entity.hvac_action == HVACAction.OFF

        # change temperature so that the heating will start
        await send_temperature_change_event(entity, 30, event_timestamp)
        await send_ext_temperature_change_event(entity, 35, event_timestamp)


        # set manual target temp
        event_timestamp = now - timedelta(minutes=7)
        with patch(
                "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await entity.async_set_temperature(temperature=25)

            fake_underlying_climate.set_hvac_action(HVACAction.COOLING) # simulate under heating
            assert entity.hvac_action == HVACAction.COOLING
            assert entity.preset_mode == PRESET_NONE # Manual mode

            # the regulated temperature should be lower
            assert entity.regulated_target_temp < entity.target_temperature
            assert entity.regulated_target_temp == 25-3 # In medium we could go up to -3 degre
            assert entity.hvac_action == HVACAction.COOLING

        # change temperature so that the regulated temperature should slow down
        event_timestamp = now - timedelta(minutes=5)
        with patch(
                "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await send_temperature_change_event(entity, 26, event_timestamp)
            await send_ext_temperature_change_event(entity, 35, event_timestamp)

            # the regulated temperature should be under
            assert entity.regulated_target_temp < entity.target_temperature
            assert entity.regulated_target_temp == 25-2 # +2.3 without round_to_nearest

            # change temperature so that the regulated temperature should slow down
        event_timestamp = now - timedelta(minutes=3)
        with patch(
                "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await send_temperature_change_event(entity, 18, event_timestamp)
            await send_ext_temperature_change_event(entity, 25, event_timestamp)

            # the regulated temperature should be greater
            assert entity.regulated_target_temp > entity.target_temperature
            assert entity.regulated_target_temp == 25+0.5 # +0.4 without round_to_nearest

@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_regulation_limitations(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
    """Test the limitations of the regulation of an over climate thermostat:
       1. test the period_min parameter: do not send regulation event too frequently
       2. test the dtemp parameter: do not send regulation event if offset temp is lower than dtemp
    """

    entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="uniqueId",
        # This is include a medium regulation, dtemp=0.5, period_min=2
        data=PARTIAL_CLIMATE_CONFIG,
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})

    # Creates the regulated VTherm over climate at t-20
    # change temperature so that the heating will start
    event_timestamp = now - timedelta(minutes=20)

    with patch(
        "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
    ), patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)
        assert entry.state is ConfigEntryState.LOADED

        def find_my_entity(entity_id) -> ClimateEntity:
            """Find my new entity"""
            component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
            for entity in component.entities:
                if entity.entity_id == entity_id:
                    return entity

        entity:ThermostatOverClimate = find_my_entity("climate.theoverclimatemockname")

        assert entity
        assert isinstance(entity, ThermostatOverClimate)

        assert entity.name == "TheOverClimateMockName"
        assert entity.is_over_climate is True
        assert entity.is_regulated is True

        # Activate the heating by changing HVACMode and temperature
        # Select a hvacmode, presence and preset
        await entity.async_set_hvac_mode(HVACMode.HEAT)
        assert entity.hvac_mode is HVACMode.HEAT

        # it is cold today
        await send_temperature_change_event(entity, 15, event_timestamp)
        await send_ext_temperature_change_event(entity, 10, event_timestamp)

        # set manual target temp (at now - 19) -> the regulation should be ignored because too early
        event_timestamp = now - timedelta(minutes=19)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await entity.async_set_temperature(temperature=18)

            fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
            assert entity.hvac_action == HVACAction.HEATING

            # the regulated temperature will change because when we set temp manually it is forced
            assert entity.regulated_target_temp == 20.

        # set manual target temp (at now - 18) -> the regulation should be taken into account
        event_timestamp = now - timedelta(minutes=18)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await entity.async_set_temperature(temperature=17)
            assert entity.regulated_target_temp > entity.target_temperature
            assert entity.regulated_target_temp == 18+1 # In strong we could go up to +3 degre. 0.7 without round_to_nearest
            old_regulated_temp = entity.regulated_target_temp

        # change temperature so that dtemp < 0.5 and time is > period_min (+ 3min)
        event_timestamp = now - timedelta(minutes=15)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await send_temperature_change_event(entity, 16, event_timestamp)
            await send_ext_temperature_change_event(entity, 10, event_timestamp)

            # the regulated temperature should be under
            assert entity.regulated_target_temp == old_regulated_temp

        # change temperature so that dtemp > 0.5 and time is > period_min (+ 3min)
        event_timestamp = now - timedelta(minutes=12)
        with patch(
            "custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
        ):
            await send_temperature_change_event(entity, 18, event_timestamp)
            await send_ext_temperature_change_event(entity, 12, event_timestamp)

            # the regulated should have been done
            assert entity.regulated_target_temp != old_regulated_temp
            assert entity.regulated_target_temp > entity.target_temperature
            assert entity.regulated_target_temp == 17 + 1.5 # 0.7 without round_to_nearest