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

""" Test the normal start of a Thermostat """
from datetime import timedelta, datetime

from homeassistant.core import HomeAssistant
from homeassistant.components.climate import HVACMode

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorStateClass,
)
from homeassistant.const import UnitOfTime, UnitOfPower, UnitOfEnergy, PERCENTAGE

from pytest_homeassistant_custom_component.common import MockConfigEntry

from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.sensor import (
    EnergySensor,
    MeanPowerSensor,
    OnPercentSensor,
    OnTimeSensor,
    OffTimeSensor,
    LastTemperatureSensor,
    LastExtTemperatureSensor,
)

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_sensors_over_switch(
    hass: HomeAssistant,
    skip_hass_states_is_state,
    skip_turn_on_off_heater,
    skip_send_event,
):
    """Test the sensors with a thermostat avec switch type"""

    entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverSwitchMockName",
        unique_id="uniqueId",
        data={
            CONF_NAME: "TheOverSwitchMockName",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
            CONF_CYCLE_MIN: 5,
            CONF_TEMP_MIN: 15,
            CONF_TEMP_MAX: 30,
            "eco_temp": 17,
            "comfort_temp": 18,
            "boost_temp": 19,
            CONF_USE_WINDOW_FEATURE: False,
            CONF_USE_MOTION_FEATURE: False,
            CONF_USE_POWER_FEATURE: False,
            CONF_USE_PRESENCE_FEATURE: False,
            CONF_HEATER: "switch.mock_switch",
            CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
            CONF_TPI_COEF_INT: 0.3,
            CONF_TPI_COEF_EXT: 0.01,
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_DEVICE_POWER: 200,
        },
    )

    entity: BaseThermostat = await create_thermostat(
        hass, entry, "climate.theoverswitchmockname"
    )
    assert entity

    energy_sensor: EnergySensor = search_entity(
        hass, "sensor.theoverswitchmockname_energy", "sensor"
    )
    assert energy_sensor

    mean_power_sensor: MeanPowerSensor = search_entity(
        hass, "sensor.theoverswitchmockname_mean_power_cycle", "sensor"
    )
    assert mean_power_sensor

    on_percent_sensor: OnPercentSensor = search_entity(
        hass, "sensor.theoverswitchmockname_power_percent", "sensor"
    )
    assert on_percent_sensor

    on_time_sensor: OnTimeSensor = search_entity(
        hass, "sensor.theoverswitchmockname_on_time", "sensor"
    )
    assert on_time_sensor

    off_time_sensor: OffTimeSensor = search_entity(
        hass, "sensor.theoverswitchmockname_off_time", "sensor"
    )
    assert off_time_sensor

    last_temperature_sensor: LastTemperatureSensor = search_entity(
        hass, "sensor.theoverswitchmockname_last_temperature_date", "sensor"
    )
    assert last_temperature_sensor

    last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity(
        hass, "sensor.theoverswitchmockname_last_external_temperature_date", "sensor"
    )
    assert last_ext_temperature_sensor

    # Simulate the event reception
    await energy_sensor.async_my_climate_changed()
    assert energy_sensor.state == 0.0
    await mean_power_sensor.async_my_climate_changed()
    assert mean_power_sensor.state == 0.0
    await on_percent_sensor.async_my_climate_changed()
    assert on_percent_sensor.state == 0.0
    await on_time_sensor.async_my_climate_changed()
    assert on_time_sensor.state == 0.0
    await off_time_sensor.async_my_climate_changed()
    assert off_time_sensor.state == 300.0
    await last_temperature_sensor.async_my_climate_changed()
    assert last_temperature_sensor.state is not None
    await last_ext_temperature_sensor.async_my_climate_changed()
    assert last_ext_temperature_sensor.state is not None

    last_temp_date = last_temperature_sensor.state

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

    event_timestamp = now - timedelta(minutes=1)

    # Start the heater to get some values
    await entity.async_set_preset_mode(PRESET_COMFORT)
    await entity.async_set_hvac_mode(HVACMode.HEAT)
    await send_temperature_change_event(entity, 15, event_timestamp)
    await send_ext_temperature_change_event(entity, 5, event_timestamp)

    entity.incremente_energy()

    await energy_sensor.async_my_climate_changed()
    assert energy_sensor.state == round(16.667, 2)
    assert energy_sensor.device_class == SensorDeviceClass.ENERGY
    assert energy_sensor.state_class == SensorStateClass.TOTAL_INCREASING
    # because device_power is 200
    assert energy_sensor.unit_of_measurement == UnitOfEnergy.WATT_HOUR

    await mean_power_sensor.async_my_climate_changed()
    assert mean_power_sensor.state == 200.0
    assert mean_power_sensor.device_class == SensorDeviceClass.POWER
    assert mean_power_sensor.state_class == SensorStateClass.MEASUREMENT
    # because device_power is 200
    assert mean_power_sensor.unit_of_measurement == UnitOfPower.WATT

    await on_percent_sensor.async_my_climate_changed()
    assert on_percent_sensor.state == 100.0
    assert on_percent_sensor.unit_of_measurement == PERCENTAGE

    await on_time_sensor.async_my_climate_changed()
    assert on_time_sensor.state == 300.0
    assert on_time_sensor.device_class == SensorDeviceClass.DURATION
    assert on_time_sensor.state_class == SensorStateClass.MEASUREMENT
    assert on_time_sensor.unit_of_measurement == UnitOfTime.SECONDS

    await off_time_sensor.async_my_climate_changed()
    assert off_time_sensor.state == 0.0
    assert off_time_sensor.device_class == SensorDeviceClass.DURATION
    assert off_time_sensor.state_class == SensorStateClass.MEASUREMENT
    assert off_time_sensor.unit_of_measurement == UnitOfTime.SECONDS

    await last_temperature_sensor.async_my_climate_changed()
    assert (
        last_temperature_sensor.state is not None
        and last_temperature_sensor.state != last_temp_date
    )
    assert last_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP

    await last_ext_temperature_sensor.async_my_climate_changed()
    assert (
        last_ext_temperature_sensor.state is not None
        and last_ext_temperature_sensor.state != last_temp_date
    )
    assert last_ext_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP

    cancel_switchs_cycles(entity)


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_sensors_over_climate(
    hass: HomeAssistant,
    skip_hass_states_is_state,
    skip_turn_on_off_heater,
    skip_send_event,
):
    """Test the sensors with thermostat over climate type"""

    the_mock_underlying = MagicMockClimate()
    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=the_mock_underlying,
    ):
        entry = MockConfigEntry(
            domain=DOMAIN,
            title="TheOverClimateMockName",
            unique_id="uniqueId",
            data={
                CONF_NAME: "TheOverClimateMockName",
                CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
                CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
                CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
                CONF_CYCLE_MIN: 5,
                CONF_TEMP_MIN: 15,
                CONF_TEMP_MAX: 30,
                "eco_temp": 17,
                "comfort_temp": 18,
                "boost_temp": 19,
                CONF_USE_WINDOW_FEATURE: False,
                CONF_USE_MOTION_FEATURE: False,
                CONF_USE_POWER_FEATURE: True,
                CONF_USE_PRESENCE_FEATURE: False,
                CONF_CLIMATE: "climate.mock_climate",
                CONF_MINIMAL_ACTIVATION_DELAY: 30,
                CONF_SECURITY_DELAY_MIN: 5,
                CONF_SECURITY_MIN_ON_PERCENT: 0.3,
                CONF_POWER_SENSOR: "sensor.mock_power_sensor",
                CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
                CONF_DEVICE_POWER: 1.5,
                CONF_PRESET_POWER: 12,
            },
        )

        entity: BaseThermostat = await create_thermostat(
            hass, entry, "climate.theoverclimatemockname"
        )
        assert entity
        assert entity.is_over_climate

    energy_sensor: EnergySensor = search_entity(
        hass, "sensor.theoverclimatemockname_energy", "sensor"
    )
    assert energy_sensor

    last_temperature_sensor: LastTemperatureSensor = search_entity(
        hass, "sensor.theoverclimatemockname_last_temperature_date", "sensor"
    )
    assert last_temperature_sensor

    last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity(
        hass, "sensor.theoverclimatemockname_last_external_temperature_date", "sensor"
    )
    assert last_ext_temperature_sensor

    # Simulate the event reception
    await energy_sensor.async_my_climate_changed()
    assert energy_sensor.state == 0.0
    await last_temperature_sensor.async_my_climate_changed()
    assert last_temperature_sensor.state is not None
    await last_ext_temperature_sensor.async_my_climate_changed()
    assert last_ext_temperature_sensor.state is not None

    last_temp_date = last_temperature_sensor.state

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

    event_timestamp = now - timedelta(minutes=1)

    # Start the heater to get some values
    await entity.async_set_preset_mode(PRESET_COMFORT)
    await entity.async_set_hvac_mode(HVACMode.HEAT)
    await send_temperature_change_event(entity, 15, event_timestamp)
    await send_ext_temperature_change_event(entity, 5, event_timestamp)

    # to add energy we must have HVACAction underlying climate event
    # Send a climate_change event with HVACAction=HEATING
    event_timestamp = now - timedelta(minutes=60)
    await send_climate_change_event(
        entity,
        new_hvac_mode=HVACMode.HEAT,
        old_hvac_mode=HVACMode.HEAT,
        new_hvac_action=HVACAction.HEATING,
        old_hvac_action=HVACAction.OFF,
        date=event_timestamp,
    )

    # Send a climate_change event with HVACAction=IDLE (end of heating)
    await send_climate_change_event(
        entity,
        new_hvac_mode=HVACMode.HEAT,
        old_hvac_mode=HVACMode.HEAT,
        new_hvac_action=HVACAction.IDLE,
        old_hvac_action=HVACAction.HEATING,
        date=now,
    )

    # 60 minutes heating with 1.5 kW heating -> 1.5 kWh
    await energy_sensor.async_my_climate_changed()
    assert energy_sensor.state == 1.5
    assert energy_sensor.device_class == SensorDeviceClass.ENERGY
    assert energy_sensor.state_class == SensorStateClass.TOTAL_INCREASING
    # because device_power is 1.5 kW
    assert energy_sensor.unit_of_measurement == UnitOfEnergy.KILO_WATT_HOUR

    entity.incremente_energy()
    await energy_sensor.async_my_climate_changed()
    assert energy_sensor.state == 3.0

    await last_temperature_sensor.async_my_climate_changed()
    assert (
        last_temperature_sensor.state is not None
        and last_temperature_sensor.state != last_temp_date
    )
    assert last_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP

    await last_ext_temperature_sensor.async_my_climate_changed()
    assert (
        last_ext_temperature_sensor.state is not None
        and last_ext_temperature_sensor.state != last_temp_date
    )
    assert last_ext_temperature_sensor.device_class == SensorDeviceClass.TIMESTAMP


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_sensors_over_climate_minimal(
    hass: HomeAssistant,
    skip_hass_states_is_state,
    skip_turn_on_off_heater,
    skip_send_event,
):
    """Test the sensors with thermostat over climate type"""

    the_mock_underlying = MagicMockClimate()
    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=the_mock_underlying,
    ):
        entry = MockConfigEntry(
            domain=DOMAIN,
            title="TheOverClimateMockName",
            unique_id="uniqueId",
            data={
                CONF_NAME: "TheOverClimateMockName",
                CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
                CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
                CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
                CONF_CYCLE_MIN: 5,
                CONF_TEMP_MIN: 15,
                CONF_TEMP_MAX: 30,
                "eco_temp": 17,
                "comfort_temp": 18,
                "boost_temp": 19,
                CONF_USE_WINDOW_FEATURE: False,
                CONF_USE_MOTION_FEATURE: False,
                CONF_USE_POWER_FEATURE: False,
                CONF_USE_PRESENCE_FEATURE: False,
                CONF_CLIMATE: "climate.mock_climate",
                CONF_MINIMAL_ACTIVATION_DELAY: 30,
                CONF_SECURITY_DELAY_MIN: 5,
                CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            },
        )

        entity: BaseThermostat = await create_thermostat(
            hass, entry, "climate.theoverclimatemockname"
        )
        assert entity
        assert entity.is_over_climate

    energy_sensor: EnergySensor = search_entity(
        hass, "sensor.theoverclimatemockname_energy", "sensor"
    )
    assert energy_sensor is None

    last_temperature_sensor: LastTemperatureSensor = search_entity(
        hass, "sensor.theoverclimatemockname_last_temperature_date", "sensor"
    )
    assert last_temperature_sensor

    last_ext_temperature_sensor: LastExtTemperatureSensor = search_entity(
        hass, "sensor.theoverclimatemockname_last_external_temperature_date", "sensor"
    )
    assert last_ext_temperature_sensor
