# pylint: disable=line-too-long

""" Implements the Safety as a Feature Manager"""

from typing import Any
from homeassistant.const import (
    STATE_ON,
    STATE_OFF,
    STATE_UNAVAILABLE,
    STATE_UNKNOWN,
)

from homeassistant.core import HomeAssistant

from homeassistant.components.climate import HVACAction


from .const import *  # pylint: disable=wildcard-import, unused-wildcard-import
from .commons import write_event_log
from .commons_type import ConfigData

from .base_manager import BaseFeatureManager
from .vtherm_central_api import VersatileThermostatAPI
from .vtherm_hvac_mode import VThermHvacMode
from vtherm_api.log_collector import get_vtherm_logger

_LOGGER = get_vtherm_logger(__name__)


class FeatureSafetyManager(BaseFeatureManager):
    """The implementation of the Safety feature"""

    unrecorded_attributes = frozenset(
        {
            "is_safety_configured",
            "safety_manager",
        }
    )

    def __init__(self, vtherm: Any, hass: HomeAssistant):
        """Init of a featureManager"""
        super().__init__(vtherm, hass)

        self._is_configured: bool = False
        self._safety_delay_min = None
        self._safety_min_on_percent = None
        self._safety_default_on_percent = None
        self._safety_state = STATE_UNAVAILABLE
        self._is_outdoor_checked = True

    @overrides
    def post_init(self, entry_infos: ConfigData):
        """Reinit of the manager"""
        self._safety_delay_min = entry_infos.get(CONF_SAFETY_DELAY_MIN)
        self._safety_min_on_percent = (
            entry_infos.get(CONF_SAFETY_MIN_ON_PERCENT)
            if entry_infos.get(CONF_SAFETY_MIN_ON_PERCENT) is not None
            else DEFAULT_SAFETY_MIN_ON_PERCENT
        )
        self._safety_default_on_percent = (
            entry_infos.get(CONF_SAFETY_DEFAULT_ON_PERCENT)
            if entry_infos.get(CONF_SAFETY_DEFAULT_ON_PERCENT) is not None
            else DEFAULT_SAFETY_DEFAULT_ON_PERCENT
        )

        if (
            self._safety_delay_min is not None
            and self._safety_default_on_percent is not None
            and self._safety_default_on_percent is not None
        ):
            self._safety_state = STATE_UNKNOWN
            self._is_configured = True

        api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
        self._is_outdoor_checked = not api.safety_mode or api.safety_mode.get("check_outdoor_sensor") is not False

        _LOGGER.info("%s - is_outdoor_checked is %s", self, self._is_outdoor_checked)

    @overrides
    async def start_listening(self):
        """Start listening the underlying entity"""

    @overrides
    def stop_listening(self):
        """Stop listening and remove the eventual timer still running"""

    @overrides
    async def refresh_state(self) -> bool:
        """Check the safety and an eventual action
        Return True is safety should be active"""

        if not self._is_configured:
            _LOGGER.debug("%s - safety is disabled (or not configured)", self)
            return False

        if self._vtherm.requested_state.hvac_mode == VThermHvacMode_OFF:
            self._safety_state = STATE_OFF
            _LOGGER.debug("%s - safety is OFF because requested_state is OFF", self)
            return False

        now = self._vtherm.now
        current_tz = dt_util.get_time_zone(self._hass.config.time_zone)

        is_safety_detected = self.is_safety_detected

        delta_temp = (
            now - self._vtherm.last_temperature_measure.replace(tzinfo=current_tz)
        ).total_seconds() / 60.0
        delta_ext_temp = (
            now - self._vtherm.last_ext_temperature_measure.replace(tzinfo=current_tz)
        ).total_seconds() / 60.0

        mode_cond = self._vtherm.hvac_mode != VThermHvacMode_OFF

        temp_cond: bool = delta_temp > self._safety_delay_min or (self._is_outdoor_checked and delta_ext_temp > self._safety_delay_min)
        climate_cond: bool = (
            self._vtherm.is_over_climate
            and self._vtherm.hvac_action
            not in [
                HVACAction.COOLING,
                HVACAction.IDLE,
            ]
        )
        switch_cond: bool = (
            not self._vtherm.is_over_climate
            and self._vtherm.has_prop
            and self._vtherm.proportional_algorithm is not None
            and self._vtherm.proportional_algorithm.calculated_on_percent
            >= self._safety_min_on_percent
        )

        _LOGGER.debug(
            "%s - checking safety delta_temp=%.1f delta_ext_temp=%.1f mod_cond=%s temp_cond=%s climate_cond=%s switch_cond=%s",
            self,
            delta_temp,
            delta_ext_temp,
            mode_cond,
            temp_cond,
            climate_cond,
            switch_cond,
        )

        # Issue 99 - a climate is regulated by the device itself and not by VTherm. So a VTherm should never be in safety !
        should_climate_be_in_safety = False  # temp_cond and climate_cond
        should_switch_be_in_safety = temp_cond and switch_cond
        should_be_in_safety = should_climate_be_in_safety or should_switch_be_in_safety

        should_start_safety = mode_cond and not is_safety_detected and should_be_in_safety
        # attr_preset_mode is not necessary normaly. It is just here to be sure
        should_stop_safety = is_safety_detected and not should_be_in_safety

        # Logging and event
        if should_start_safety:
            if should_climate_be_in_safety:
                _LOGGER.warning(
                    "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Setting it into safety mode",
                    self,
                    self._safety_delay_min,
                    delta_temp,
                    delta_ext_temp,
                    self._vtherm.hvac_action,
                )
            elif should_switch_be_in_safety:
                _LOGGER.warning(
                    "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f %%) is over defined value (%.2f %%). Set it into safety mode",
                    self,
                    self._safety_delay_min,
                    delta_temp,
                    delta_ext_temp,
                    self._vtherm.proportional_algorithm.on_percent * 100,
                    self._safety_min_on_percent * 100 if self._safety_min_on_percent is not None else 0,
                )

            self._vtherm.send_event(
                EventType.TEMPERATURE_EVENT,
                {
                    "last_temperature_measure": self._vtherm.last_temperature_measure.replace(
                        tzinfo=current_tz
                    ).isoformat(),
                    "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace(
                        tzinfo=current_tz
                    ).isoformat(),
                    "current_temp": self._vtherm.current_temperature,
                    "current_ext_temp": self._vtherm.current_outdoor_temperature,
                    "target_temp": self._vtherm.target_temperature,
                },
            )

        # Start safety mode
        if should_start_safety:
            write_event_log(_LOGGER, self._vtherm, "Starting safety mode")
            self._safety_state = STATE_ON
            # self._vtherm.save_hvac_mode()
            # self._vtherm.save_preset_mode()
            if self._vtherm.has_prop:
                self._vtherm.set_safety(self._safety_default_on_percent)

            self._vtherm.send_event(
                EventType.SAFETY_EVENT,
                {
                    "type": "start",
                    "last_temperature_measure": self._vtherm.last_temperature_measure.replace(tzinfo=current_tz).isoformat(),
                    "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace(tzinfo=current_tz).isoformat(),
                    "current_temp": self._vtherm.current_temperature,
                    "current_ext_temp": self._vtherm.current_outdoor_temperature,
                    "target_temp": self._vtherm.target_temperature,
                },
            )

        # Stop safety mode
        elif should_stop_safety:
            write_event_log(_LOGGER, self._vtherm, "Ending safety mode")
            _LOGGER.warning("%s - End of safety mode.", self)
            self._safety_state = STATE_OFF
            if self._vtherm.has_prop:
                self._vtherm.unset_safety()
            self._vtherm.send_event(
                EventType.SAFETY_EVENT,
                {
                    "type": "end",
                    "last_temperature_measure": self._vtherm.last_temperature_measure.replace(tzinfo=current_tz).isoformat(),
                    "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace(tzinfo=current_tz).isoformat(),
                    "current_temp": self._vtherm.current_temperature,
                    "current_ext_temp": self._vtherm.current_outdoor_temperature,
                    "target_temp": self._vtherm.target_temperature,
                },
            )

        # Initialize the safety_state if not already done
        elif not should_be_in_safety and self._safety_state in [STATE_UNKNOWN]:
            self._safety_state = STATE_OFF

        return self._safety_state == STATE_ON

    async def _async_update_states_later(self, _=None):
        """Called at next tick to update states without recursion"""
        self._vtherm.requested_state.force_changed()
        await self._vtherm.update_states(force=True)

    async def refresh_and_update_if_changed(self) -> bool:
        """Refresh the safety state and update_states of VTherm if changed
        Returns True if the state has changed, False otherwise"""
        old_safety: bool = self.is_safety_detected
        if old_safety != await self.refresh_state():
            # issue 1450 - schedule update_states at next tick to avoid recursion
            self._hass.async_create_task(self._async_update_states_later())
            return True

        return False

    def add_custom_attributes(self, extra_state_attributes: dict[str, Any]):
        """Add some custom attributes"""

        extra_state_attributes.update(
            {
                "is_safety_configured": self._is_configured,
            }
        )

        if self._is_configured:
            extra_state_attributes.update(
                {
                    "safety_manager": {
                        "safety_state": self._safety_state,
                        "safety_delay_min": self._safety_delay_min,
                        "safety_min_on_percent": self._safety_min_on_percent,
                        "safety_default_on_percent": self._safety_default_on_percent,
                    }
                }
            )

    @property
    def is_configured(self) -> bool:
        """Return True of the safety feature is configured"""
        return self._is_configured

    def set_safety_delay_min(self, safety_delay_min):
        """Set the delay min"""
        self._safety_delay_min = safety_delay_min

    def set_safety_min_on_percent(self, safety_min_on_percent):
        """Set the min on percent"""
        self._safety_min_on_percent = safety_min_on_percent

    def set_safety_default_on_percent(self, safety_default_on_percent):
        """Set the default on_percent"""
        self._safety_default_on_percent = safety_default_on_percent

    @property
    def is_safety_detected(self) -> bool:
        """Returns the is vtherm is in safety mode"""
        return self._safety_state == STATE_ON

    @property
    def safety_state(self) -> str:
        """Returns the safety state: STATE_ON, STATE_OFF, STATE_UNKWNON, STATE_UNAVAILABLE"""
        return self._safety_state

    @property
    def safety_delay_min(self) -> bool:
        """Returns the safety delay min"""
        return self._safety_delay_min

    @property
    def safety_min_on_percent(self) -> bool:
        """Returns the safety min on percent"""
        return self._safety_min_on_percent

    @property
    def safety_default_on_percent(self) -> bool:
        """Returns the safety safety_default_on_percent"""
        return self._safety_default_on_percent

    @property
    def is_detected(self) -> bool:
        """Return the overall state of the feature manager based on safety states"""
        return self.is_safety_detected

    def __str__(self):
        return f"SafetyManager-{self.name}"
