"""Provides triggers for climates."""

from typing import override

import voluptuous as vol

from homeassistant.const import CONF_OPTIONS, UnitOfTemperature
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.trigger import (
    ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR,
    EntityNumericalStateChangedTriggerBase,
    EntityNumericalStateChangedTriggerWithUnitBase,
    EntityNumericalStateCrossedThresholdTriggerBase,
    EntityNumericalStateCrossedThresholdTriggerWithUnitBase,
    EntityNumericalStateTriggerBase,
    EntityNumericalStateTriggerWithUnitBase,
    EntityTargetStateTriggerBase,
    Trigger,
    TriggerConfig,
    make_entity_target_state_trigger,
    make_entity_transition_trigger,
)
from homeassistant.util.unit_conversion import TemperatureConverter

from .const import DOMAIN, ClimateEntityStateAttribute, HVACAction, HVACMode

CONF_HVAC_MODE = "hvac_mode"

HVAC_MODE_CHANGED_TRIGGER_SCHEMA = ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR.extend(
    {
        vol.Required(CONF_OPTIONS): {
            vol.Required(CONF_HVAC_MODE): vol.All(
                cv.ensure_list, vol.Length(min=1), [vol.Coerce(HVACMode)]
            ),
        },
    }
)


class HVACModeChangedTrigger(EntityTargetStateTriggerBase):
    """Trigger for entity state changes."""

    _domain_specs = {DOMAIN: DomainSpec()}
    _schema = HVAC_MODE_CHANGED_TRIGGER_SCHEMA

    def __init__(self, hass: HomeAssistant, config: TriggerConfig) -> None:
        """Initialize the state trigger."""
        super().__init__(hass, config)
        self._to_states = set(self._options[CONF_HVAC_MODE])


class _ClimateTargetTemperatureTriggerMixin(EntityNumericalStateTriggerWithUnitBase):
    """Mixin for climate target temperature triggers with unit conversion."""

    _base_unit = UnitOfTemperature.CELSIUS
    _domain_specs = {
        DOMAIN: DomainSpec(value_source=ClimateEntityStateAttribute.TARGET_TEMPERATURE)
    }
    _unit_converter = TemperatureConverter

    @override
    def _should_include(self, state: State) -> bool:
        """Skip climate entities that do not expose a target temperature."""
        return (
            super()._should_include(state)
            and state.attributes.get(ClimateEntityStateAttribute.TARGET_TEMPERATURE)
            is not None
        )

    @override
    def _get_entity_unit(self, state: State) -> str | None:
        """Get the temperature unit of a climate entity from its state."""
        # Climate entities convert temperatures to the system unit via show_temp
        return self._hass.config.units.temperature_unit


class ClimateTargetTemperatureChangedTrigger(
    _ClimateTargetTemperatureTriggerMixin,
    EntityNumericalStateChangedTriggerWithUnitBase,
):
    """Trigger for climate target temperature value changes."""


class ClimateTargetTemperatureCrossedThresholdTrigger(
    _ClimateTargetTemperatureTriggerMixin,
    EntityNumericalStateCrossedThresholdTriggerWithUnitBase,
):
    """Trigger for climate target temperature value crossing a threshold."""


class _ClimateTargetHumidityTriggerMixin(EntityNumericalStateTriggerBase):
    """Mixin for climate target humidity triggers."""

    _domain_specs = {
        DOMAIN: DomainSpec(value_source=ClimateEntityStateAttribute.TARGET_HUMIDITY)
    }
    _valid_unit = "%"

    @override
    def _should_include(self, state: State) -> bool:
        """Skip climate entities that do not expose a target humidity."""
        return (
            super()._should_include(state)
            and state.attributes.get(ClimateEntityStateAttribute.TARGET_HUMIDITY)
            is not None
        )


class ClimateTargetHumidityChangedTrigger(
    _ClimateTargetHumidityTriggerMixin, EntityNumericalStateChangedTriggerBase
):
    """Trigger for climate target humidity value changes."""


class ClimateTargetHumidityCrossedThresholdTrigger(
    _ClimateTargetHumidityTriggerMixin, EntityNumericalStateCrossedThresholdTriggerBase
):
    """Trigger for climate target humidity value crossing a threshold."""


TRIGGERS: dict[str, type[Trigger]] = {
    "hvac_mode_changed": HVACModeChangedTrigger,
    "started_cooling": make_entity_target_state_trigger(
        {DOMAIN: DomainSpec(value_source=ClimateEntityStateAttribute.HVAC_ACTION)},
        HVACAction.COOLING,
    ),
    "started_drying": make_entity_target_state_trigger(
        {DOMAIN: DomainSpec(value_source=ClimateEntityStateAttribute.HVAC_ACTION)},
        HVACAction.DRYING,
    ),
    "target_humidity_changed": ClimateTargetHumidityChangedTrigger,
    "target_humidity_crossed_threshold": ClimateTargetHumidityCrossedThresholdTrigger,
    "target_temperature_changed": ClimateTargetTemperatureChangedTrigger,
    "target_temperature_crossed_threshold": (
        ClimateTargetTemperatureCrossedThresholdTrigger
    ),
    "turned_off": make_entity_target_state_trigger(DOMAIN, HVACMode.OFF),
    "turned_on": make_entity_transition_trigger(
        DOMAIN,
        from_states={
            HVACMode.OFF,
        },
        to_states={
            HVACMode.AUTO,
            HVACMode.COOL,
            HVACMode.DRY,
            HVACMode.FAN_ONLY,
            HVACMode.HEAT,
            HVACMode.HEAT_COOL,
        },
    ),
    "started_heating": make_entity_target_state_trigger(
        {DOMAIN: DomainSpec(value_source=ClimateEntityStateAttribute.HVAC_ACTION)},
        HVACAction.HEATING,
    ),
}


async def async_get_triggers(hass: HomeAssistant) -> dict[str, type[Trigger]]:
    """Return the triggers for climates."""
    return TRIGGERS
