"""Template platform that aggregates meteorological data."""

from collections.abc import Callable
from dataclasses import asdict, dataclass
import logging
from typing import Any, Literal, Self, override

import voluptuous as vol

from homeassistant.components.weather import (
    ATTR_CONDITION_CLEAR_NIGHT,
    ATTR_CONDITION_CLOUDY,
    ATTR_CONDITION_EXCEPTIONAL,
    ATTR_CONDITION_FOG,
    ATTR_CONDITION_HAIL,
    ATTR_CONDITION_LIGHTNING,
    ATTR_CONDITION_LIGHTNING_RAINY,
    ATTR_CONDITION_PARTLYCLOUDY,
    ATTR_CONDITION_POURING,
    ATTR_CONDITION_RAINY,
    ATTR_CONDITION_SNOWY,
    ATTR_CONDITION_SNOWY_RAINY,
    ATTR_CONDITION_SUNNY,
    ATTR_CONDITION_WINDY,
    ATTR_CONDITION_WINDY_VARIANT,
    DOMAIN as WEATHER_DOMAIN,
    ENTITY_ID_FORMAT,
    Forecast,
    WeatherEntity,
    WeatherEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    CONF_CONDITION,
    CONF_ENTITY_PICTURE_TEMPLATE,
    CONF_FRIENDLY_NAME,
    CONF_ICON,
    CONF_ICON_TEMPLATE,
    CONF_NAME,
    CONF_TEMPERATURE_UNIT,
)
from homeassistant.core import HomeAssistant, State, callback
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity_platform import (
    AddConfigEntryEntitiesCallback,
    AddEntitiesCallback,
)
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util.unit_conversion import (
    DistanceConverter,
    PressureConverter,
    SpeedConverter,
    TemperatureConverter,
)

from . import TriggerUpdateCoordinator, validators as template_validators
from .const import CONF_AVAILABILITY, CONF_AVAILABILITY_TEMPLATE, CONF_PICTURE
from .entity import AbstractTemplateEntity
from .helpers import (
    async_create_platform_template_not_supported_issue,
    async_setup_template_entry,
    async_setup_template_platform,
    async_setup_template_preview,
)
from .schemas import (
    TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
    make_template_entity_common_modern_schema,
)
from .template_entity import TemplateEntity
from .trigger_entity import TriggerEntity

_LOGGER = logging.getLogger(__name__)

CHECK_FORECAST_KEYS = (
    set()
    .union(Forecast.__annotations__.keys())
    # Manually add the forecast resulting attributes that only exists
    #  as native_* in the Forecast definition
    .union(("apparent_temperature", "wind_gust_speed", "dew_point"))
)

CONDITION_CLASSES = [
    ATTR_CONDITION_CLEAR_NIGHT,
    ATTR_CONDITION_CLOUDY,
    ATTR_CONDITION_EXCEPTIONAL,
    ATTR_CONDITION_FOG,
    ATTR_CONDITION_HAIL,
    ATTR_CONDITION_LIGHTNING_RAINY,
    ATTR_CONDITION_LIGHTNING,
    ATTR_CONDITION_PARTLYCLOUDY,
    ATTR_CONDITION_POURING,
    ATTR_CONDITION_RAINY,
    ATTR_CONDITION_SNOWY_RAINY,
    ATTR_CONDITION_SNOWY,
    ATTR_CONDITION_SUNNY,
    ATTR_CONDITION_WINDY_VARIANT,
    ATTR_CONDITION_WINDY,
]

CONF_APPARENT_TEMPERATURE = "apparent_temperature"
CONF_APPARENT_TEMPERATURE_TEMPLATE = "apparent_temperature_template"
CONF_ATTRIBUTION = "attribution"
CONF_ATTRIBUTION_TEMPLATE = "attribution_template"
CONF_CLOUD_COVERAGE = "cloud_coverage"
CONF_CLOUD_COVERAGE_TEMPLATE = "cloud_coverage_template"
CONF_CONDITION_TEMPLATE = "condition_template"
CONF_DEW_POINT = "dew_point"
CONF_DEW_POINT_TEMPLATE = "dew_point_template"
CONF_FORECAST_DAILY = "forecast_daily"
CONF_FORECAST_DAILY_TEMPLATE = "forecast_daily_template"
CONF_FORECAST_HOURLY = "forecast_hourly"
CONF_FORECAST_HOURLY_TEMPLATE = "forecast_hourly_template"
CONF_FORECAST_TWICE_DAILY = "forecast_twice_daily"
CONF_FORECAST_TWICE_DAILY_TEMPLATE = "forecast_twice_daily_template"
CONF_HUMIDITY = "humidity"
CONF_HUMIDITY_TEMPLATE = "humidity_template"
CONF_OZONE = "ozone"
CONF_OZONE_TEMPLATE = "ozone_template"
CONF_PRECIPITATION_UNIT = "precipitation_unit"
CONF_PRESSURE = "pressure"
CONF_PRESSURE_TEMPLATE = "pressure_template"
CONF_PRESSURE_UNIT = "pressure_unit"
CONF_TEMPERATURE = "temperature"
CONF_TEMPERATURE_TEMPLATE = "temperature_template"
CONF_UV_INDEX = "uv_index"
CONF_UV_INDEX_TEMPLATE = "uv_index_template"
CONF_VISIBILITY = "visibility"
CONF_VISIBILITY_TEMPLATE = "visibility_template"
CONF_VISIBILITY_UNIT = "visibility_unit"
CONF_WEATHER = "weather"
CONF_WIND_BEARING = "wind_bearing"
CONF_WIND_BEARING_TEMPLATE = "wind_bearing_template"
CONF_WIND_GUST_SPEED = "wind_gust_speed"
CONF_WIND_GUST_SPEED_TEMPLATE = "wind_gust_speed_template"
CONF_WIND_SPEED = "wind_speed"
CONF_WIND_SPEED_TEMPLATE = "wind_speed_template"
CONF_WIND_SPEED_UNIT = "wind_speed_unit"

DEFAULT_NAME = "Template Weather"

LEGACY_OPTIONS = {
    CONF_ICON_TEMPLATE: CONF_ICON,
    CONF_ENTITY_PICTURE_TEMPLATE: CONF_PICTURE,
    CONF_AVAILABILITY_TEMPLATE: CONF_AVAILABILITY,
    CONF_FRIENDLY_NAME: CONF_NAME,
    CONF_APPARENT_TEMPERATURE_TEMPLATE: CONF_APPARENT_TEMPERATURE,
    CONF_ATTRIBUTION_TEMPLATE: CONF_ATTRIBUTION,
    CONF_CLOUD_COVERAGE_TEMPLATE: CONF_CLOUD_COVERAGE,
    CONF_CONDITION_TEMPLATE: CONF_CONDITION,
    CONF_DEW_POINT_TEMPLATE: CONF_DEW_POINT,
    CONF_FORECAST_DAILY_TEMPLATE: CONF_FORECAST_DAILY,
    CONF_FORECAST_HOURLY_TEMPLATE: CONF_FORECAST_HOURLY,
    CONF_FORECAST_TWICE_DAILY_TEMPLATE: CONF_FORECAST_TWICE_DAILY,
    CONF_HUMIDITY_TEMPLATE: CONF_HUMIDITY,
    CONF_OZONE_TEMPLATE: CONF_OZONE,
    CONF_PRESSURE_TEMPLATE: CONF_PRESSURE,
    CONF_TEMPERATURE_TEMPLATE: CONF_TEMPERATURE,
    CONF_UV_INDEX_TEMPLATE: CONF_UV_INDEX,
    CONF_VISIBILITY_TEMPLATE: CONF_VISIBILITY,
    CONF_WIND_BEARING_TEMPLATE: CONF_WIND_BEARING,
    CONF_WIND_GUST_SPEED_TEMPLATE: CONF_WIND_GUST_SPEED,
    CONF_WIND_SPEED_TEMPLATE: CONF_WIND_SPEED,
}

# These options that are templates all have _template. These fields will
# enter deprecation after legacy templates are removed.
WEATHER_COMMON_LEGACY_SCHEMA = vol.Schema(
    {
        vol.Optional(CONF_APPARENT_TEMPERATURE_TEMPLATE): cv.template,
        vol.Optional(CONF_ATTRIBUTION_TEMPLATE): cv.template,
        vol.Optional(CONF_CLOUD_COVERAGE_TEMPLATE): cv.template,
        vol.Required(CONF_CONDITION_TEMPLATE): cv.template,
        vol.Optional(CONF_DEW_POINT_TEMPLATE): cv.template,
        vol.Optional(CONF_FORECAST_DAILY_TEMPLATE): cv.template,
        vol.Optional(CONF_FORECAST_HOURLY_TEMPLATE): cv.template,
        vol.Optional(CONF_FORECAST_TWICE_DAILY_TEMPLATE): cv.template,
        vol.Required(CONF_HUMIDITY_TEMPLATE): cv.template,
        vol.Optional(CONF_OZONE_TEMPLATE): cv.template,
        vol.Optional(CONF_PRECIPITATION_UNIT): vol.In(DistanceConverter.VALID_UNITS),
        vol.Optional(CONF_PRESSURE_TEMPLATE): cv.template,
        vol.Optional(CONF_PRESSURE_UNIT): vol.In(PressureConverter.VALID_UNITS),
        vol.Required(CONF_TEMPERATURE_TEMPLATE): cv.template,
        vol.Optional(CONF_TEMPERATURE_UNIT): vol.In(TemperatureConverter.VALID_UNITS),
        vol.Optional(CONF_VISIBILITY_TEMPLATE): cv.template,
        vol.Optional(CONF_VISIBILITY_UNIT): vol.In(DistanceConverter.VALID_UNITS),
        vol.Optional(CONF_WIND_BEARING_TEMPLATE): cv.template,
        vol.Optional(CONF_WIND_GUST_SPEED_TEMPLATE): cv.template,
        vol.Optional(CONF_WIND_SPEED_TEMPLATE): cv.template,
        vol.Optional(CONF_WIND_SPEED_UNIT): vol.In(SpeedConverter.VALID_UNITS),
    }
)

WEATHER_COMMON_MODERN_SCHEMA = vol.Schema(
    {
        vol.Optional(CONF_APPARENT_TEMPERATURE): cv.template,
        vol.Optional(CONF_ATTRIBUTION): cv.template,
        vol.Optional(CONF_CLOUD_COVERAGE): cv.template,
        vol.Required(CONF_CONDITION): cv.template,
        vol.Optional(CONF_DEW_POINT): cv.template,
        vol.Optional(CONF_FORECAST_DAILY): cv.template,
        vol.Optional(CONF_FORECAST_HOURLY): cv.template,
        vol.Optional(CONF_FORECAST_TWICE_DAILY): cv.template,
        vol.Required(CONF_HUMIDITY): cv.template,
        vol.Optional(CONF_OZONE): cv.template,
        vol.Optional(CONF_PRECIPITATION_UNIT): vol.In(DistanceConverter.VALID_UNITS),
        vol.Optional(CONF_PRESSURE): cv.template,
        vol.Optional(CONF_PRESSURE_UNIT): vol.In(PressureConverter.VALID_UNITS),
        vol.Required(CONF_TEMPERATURE): cv.template,
        vol.Optional(CONF_TEMPERATURE_UNIT): vol.In(TemperatureConverter.VALID_UNITS),
        vol.Optional(CONF_UV_INDEX): cv.template,
        vol.Optional(CONF_VISIBILITY): cv.template,
        vol.Optional(CONF_VISIBILITY_UNIT): vol.In(DistanceConverter.VALID_UNITS),
        vol.Optional(CONF_WIND_BEARING): cv.template,
        vol.Optional(CONF_WIND_GUST_SPEED): cv.template,
        vol.Optional(CONF_WIND_SPEED): cv.template,
        vol.Optional(CONF_WIND_SPEED_UNIT): vol.In(SpeedConverter.VALID_UNITS),
    }
)


WEATHER_YAML_SCHEMA = (
    vol.Schema(
        {
            vol.Optional(CONF_UV_INDEX_TEMPLATE): cv.template,
        }
    )
    .extend(WEATHER_COMMON_LEGACY_SCHEMA.schema)
    .extend(
        make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
    )
)

WEATHER_MODERN_YAML_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
    make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
)

WEATHER_CONFIG_ENTRY_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
    TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
)


def rewrite_legacy_options_to_modern_options(
    hass: HomeAssistant,
    entity_cfg: dict[str, Any],
) -> dict[str, Any]:
    """Rewrite legacy config."""
    entity_cfg = {**entity_cfg}

    for from_key, to_key in LEGACY_OPTIONS.items():
        if from_key not in entity_cfg or to_key in entity_cfg:
            continue

        val = entity_cfg.pop(from_key)
        if isinstance(val, str):
            val = template.Template(val, hass)
        entity_cfg[to_key] = val

    if CONF_NAME in entity_cfg and isinstance(entity_cfg[CONF_NAME], str):
        entity_cfg[CONF_NAME] = template.Template(entity_cfg[CONF_NAME], hass)

    return entity_cfg


async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Template weather."""

    # Rewrite the configuration options to modern keys.
    if discovery_info is None:
        async_create_platform_template_not_supported_issue(hass, WEATHER_DOMAIN)
        return

    # Modern and Trigger
    entity_configs: list[ConfigType] = discovery_info["entities"]
    modified_entity_configs = []
    for entity_config in entity_configs:
        entity_config = rewrite_legacy_options_to_modern_options(hass, entity_config)

        modified_entity_configs.append(entity_config)

    if modified_entity_configs:
        discovery_info["entities"] = modified_entity_configs

    await async_setup_template_platform(
        hass,
        WEATHER_DOMAIN,
        config,
        StateWeatherEntity,
        TriggerWeatherEntity,
        async_add_entities,
        discovery_info,
    )


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Initialize config entry."""
    await async_setup_template_entry(
        hass,
        config_entry,
        async_add_entities,
        StateWeatherEntity,
        WEATHER_CONFIG_ENTRY_SCHEMA,
    )


@callback
def async_create_preview_weather(
    hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateWeatherEntity:
    """Create a preview."""
    return async_setup_template_preview(
        hass,
        name,
        config,
        StateWeatherEntity,
        WEATHER_CONFIG_ENTRY_SCHEMA,
    )


def validate_forecast(
    entity: AbstractTemplateWeather,
    option: str,
    forecast_type: Literal["daily", "hourly", "twice_daily"],
) -> Callable[[Any], list[Forecast] | None]:
    """Validate a forecast."""

    weather_message = (
        "see Weather documentation https://www.home-assistant.io/integrations/weather/"
    )

    def validate(result: Any) -> list[Forecast] | None:
        if template_validators.check_result_for_none(result):
            return None

        if not isinstance(result, list):
            template_validators.log_validation_result_error(
                entity,
                option,
                result,
                f"expected a list, {weather_message}",
            )

        raised = False
        for forecast in result:
            if not isinstance(forecast, dict):
                raised = True
                template_validators.log_validation_result_error(
                    entity,
                    option,
                    result,
                    "expected a list of forecast"
                    f" dictionaries, got {forecast},"
                    f" {weather_message}",
                )
                continue

            diff_result = set().union(forecast.keys()).difference(CHECK_FORECAST_KEYS)
            if diff_result:
                raised = True
                template_validators.log_validation_result_error(
                    entity,
                    option,
                    result,
                    "expected valid forecast keys,"
                    f" unallowed keys: ({diff_result})"
                    f" for {forecast}, {weather_message}",
                )
            if forecast_type == "twice_daily" and "is_daytime" not in forecast:
                raised = True
                template_validators.log_validation_result_error(
                    entity,
                    option,
                    result,
                    "`is_daytime` is missing in"
                    f" twice_daily forecast {forecast},"
                    f" {weather_message}",
                )
            if "datetime" not in forecast:
                raised = True
                template_validators.log_validation_result_error(
                    entity,
                    option,
                    result,
                    "`datetime` is missing in forecast,"
                    f" got {forecast}, {weather_message}",
                )

        if raised:
            return None

        return result

    return validate


@dataclass(kw_only=True)
class WeatherExtraStoredData(ExtraStoredData):
    """Object to hold extra stored data."""

    last_apparent_temperature: float | None
    last_cloud_coverage: int | None
    last_dew_point: float | None
    last_humidity: float | None
    last_ozone: float | None
    last_pressure: float | None
    last_temperature: float | None
    last_uv_index: float | None
    last_visibility: float | None
    last_wind_bearing: float | str | None
    last_wind_gust_speed: float | None
    last_wind_speed: float | None

    @override
    def as_dict(self) -> dict[str, Any]:
        """Return a dict representation of the event data."""
        return asdict(self)

    @classmethod
    def from_dict(cls, restored: dict[str, Any]) -> Self | None:
        """Initialize a stored event state from a dict."""
        for key, vtypes in (
            ("last_apparent_temperature", (float, int)),
            ("last_cloud_coverage", (float, int)),
            ("last_dew_point", (float, int)),
            ("last_humidity", (float, int)),
            ("last_ozone", (float, int)),
            ("last_pressure", (float, int)),
            ("last_temperature", (float, int)),
            ("last_uv_index", (float, int)),
            ("last_visibility", (float, int)),
            ("last_wind_bearing", (float, int, str)),
            ("last_wind_gust_speed", (float, int)),
            ("last_wind_speed", (float, int)),
        ):
            # This is needed to safeguard against previous restore data that has strings
            # instead of floats or ints.
            if key not in restored or (
                (value := restored[key]) is not None and not isinstance(value, vtypes)
            ):
                return None

        return cls(
            last_apparent_temperature=restored["last_apparent_temperature"],
            last_cloud_coverage=restored["last_cloud_coverage"],
            last_dew_point=restored["last_dew_point"],
            last_humidity=restored["last_humidity"],
            last_ozone=restored["last_ozone"],
            last_pressure=restored["last_pressure"],
            last_temperature=restored["last_temperature"],
            last_uv_index=restored["last_uv_index"],
            last_visibility=restored["last_visibility"],
            last_wind_bearing=restored["last_wind_bearing"],
            last_wind_gust_speed=restored["last_wind_gust_speed"],
            last_wind_speed=restored["last_wind_speed"],
        )


class AbstractTemplateWeather(AbstractTemplateEntity, WeatherEntity, RestoreEntity):
    """Representation of a template weathers features."""

    _entity_id_format = ENTITY_ID_FORMAT
    _state_option = CONF_CONDITION
    _optimistic_entity = True
    _restore_state_extra_data = WeatherExtraStoredData
    _restore_state_properties = ("_attr_condition",)

    # The super init is not called because TemplateEntity
    # and TriggerEntity will call
    # AbstractTemplateEntity.__init__. This ensures that
    # the __init__ on AbstractTemplateEntity is not
    # called twice.
    def __init__(  # pylint: disable=super-init-not-called
        self, config: dict[str, Any]
    ) -> None:
        """Initialize the features."""

        # Required options
        self.setup_state_template(
            "_attr_condition",
            template_validators.item_in_list(self, CONF_CONDITION, CONDITION_CLASSES),
        )
        self.setup_template(
            CONF_HUMIDITY,
            "_attr_humidity",
            template_validators.number(self, CONF_HUMIDITY, 0.0, 100.0),
        )
        self.setup_template(
            CONF_TEMPERATURE,
            "_attr_native_temperature",
            template_validators.number(self, CONF_TEMPERATURE),
        )

        # Optional options

        self.setup_template(
            CONF_ATTRIBUTION,
            "_attribution",
            vol.Coerce(str),
        )
        self.setup_template(
            CONF_WIND_BEARING, "_attr_wind_bearing", None, self._update_wind_bearing
        )

        # Optional numeric options
        for option, attribute in (
            (CONF_APPARENT_TEMPERATURE, "_attr_native_apparent_temperature"),
            (CONF_CLOUD_COVERAGE, "_attr_cloud_coverage"),
            (CONF_DEW_POINT, "_attr_native_dew_point"),
            (CONF_OZONE, "_attr_ozone"),
            (CONF_PRESSURE, "_attr_native_pressure"),
            (CONF_UV_INDEX, "_attr_uv_index"),
            (CONF_VISIBILITY, "_attr_native_visibility"),
            (CONF_WIND_GUST_SPEED, "_attr_native_wind_gust_speed"),
            (CONF_WIND_SPEED, "_attr_native_wind_speed"),
        ):
            self.setup_template(
                option, attribute, template_validators.number(self, option)
            )

        # Forecasts

        self._forecast_daily: list[Forecast] | None = []
        self.setup_template(
            CONF_FORECAST_DAILY,
            "_forecast_daily",
            validate_forecast(self, CONF_FORECAST_DAILY, "daily"),
            self._update_forecast("daily"),
        )

        self._forecast_hourly: list[Forecast] | None = []
        self.setup_template(
            CONF_FORECAST_HOURLY,
            "_forecast_hourly",
            validate_forecast(self, CONF_FORECAST_HOURLY, "hourly"),
            self._update_forecast("hourly"),
        )

        self._forecast_twice_daily: list[Forecast] | None = []
        self.setup_template(
            CONF_FORECAST_TWICE_DAILY,
            "_forecast_twice_daily",
            validate_forecast(self, CONF_FORECAST_TWICE_DAILY, "twice_daily"),
            self._update_forecast("twice_daily"),
        )

        # Legacy support
        self._attribution: str | None = None

        # Native units
        self._attr_native_precipitation_unit = config.get(CONF_PRECIPITATION_UNIT)
        self._attr_native_pressure_unit = config.get(CONF_PRESSURE_UNIT)
        self._attr_native_temperature_unit = config.get(CONF_TEMPERATURE_UNIT)
        self._attr_native_visibility_unit = config.get(CONF_VISIBILITY_UNIT)
        self._attr_native_wind_speed_unit = config.get(CONF_WIND_SPEED_UNIT)

        # Supported Features
        self._attr_supported_features = 0
        if CONF_FORECAST_DAILY in self._templates:
            self._attr_supported_features |= WeatherEntityFeature.FORECAST_DAILY
        if CONF_FORECAST_HOURLY in self._templates:
            self._attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
        if CONF_FORECAST_TWICE_DAILY in self._templates:
            self._attr_supported_features |= WeatherEntityFeature.FORECAST_TWICE_DAILY

    @property
    @override
    def attribution(self) -> str | None:
        """Return the attribution."""
        if self._attribution is None:
            return "Powered by Home Assistant"
        return self._attribution

    @callback
    def _update_wind_bearing(self, result: Any) -> None:
        try:
            self._attr_wind_bearing = vol.Coerce(float)(result)
        except vol.Invalid:
            self._attr_wind_bearing = vol.Coerce(str)(result)

    @callback
    def _update_forecast(
        self,
        forecast_type: Literal["daily", "hourly", "twice_daily"],
    ) -> Callable[[list[Forecast] | None], None]:
        """Save template result and trigger forecast listener."""

        def update(result: list[Forecast] | None) -> None:
            setattr(self, f"_forecast_{forecast_type}", result)
            self.hass.async_create_task(
                self.async_update_listeners([forecast_type]), eager_start=True
            )

        return update

    @override
    async def async_forecast_daily(self) -> list[Forecast]:
        """Return the daily forecast in native units."""
        return self._forecast_daily or []

    @override
    async def async_forecast_hourly(self) -> list[Forecast]:
        """Return the daily forecast in native units."""
        return self._forecast_hourly or []

    @override
    async def async_forecast_twice_daily(self) -> list[Forecast]:
        """Return the daily forecast in native units."""
        return self._forecast_twice_daily or []

    @property
    @override
    def extra_restore_state_data(self) -> WeatherExtraStoredData:
        """Return weather specific state data to be restored."""
        return WeatherExtraStoredData(
            last_apparent_temperature=self.native_apparent_temperature,
            last_cloud_coverage=self._attr_cloud_coverage,
            last_dew_point=self.native_dew_point,
            last_humidity=self.humidity,
            last_ozone=self.ozone,
            last_pressure=self.native_pressure,
            last_temperature=self.native_temperature,
            last_uv_index=self.uv_index,
            last_visibility=self.native_visibility,
            last_wind_bearing=self.wind_bearing,
            last_wind_gust_speed=self.native_wind_gust_speed,
            last_wind_speed=self.native_wind_speed,
        )

    @override
    def restore_last_state_state(self, last_state: State) -> bool:
        """Restore the state from the last state."""
        self._attr_condition = last_state.state
        return True

    @override
    def restore_extra_data(self, extra_data: WeatherExtraStoredData) -> None:
        """Restore the extra data."""
        self._attr_native_apparent_temperature = extra_data.last_apparent_temperature
        self._attr_cloud_coverage = extra_data.last_cloud_coverage
        self._attr_native_dew_point = extra_data.last_dew_point
        self._attr_humidity = extra_data.last_humidity
        self._attr_ozone = extra_data.last_ozone
        self._attr_native_pressure = extra_data.last_pressure
        self._attr_native_temperature = extra_data.last_temperature
        self._attr_uv_index = extra_data.last_uv_index
        self._attr_native_visibility = extra_data.last_visibility
        self._attr_wind_bearing = extra_data.last_wind_bearing
        self._attr_native_wind_gust_speed = extra_data.last_wind_gust_speed
        self._attr_native_wind_speed = extra_data.last_wind_speed


class StateWeatherEntity(TemplateEntity, AbstractTemplateWeather):
    """Representation of a Template weather."""

    _attr_should_poll = False

    def __init__(
        self,
        hass: HomeAssistant,
        config: ConfigType,
        unique_id: str | None,
    ) -> None:
        """Initialize the Template weather."""
        TemplateEntity.__init__(self, hass, config, unique_id)
        AbstractTemplateWeather.__init__(self, config)


class TriggerWeatherEntity(TriggerEntity, AbstractTemplateWeather):
    """Weather entity based on trigger data."""

    domain = WEATHER_DOMAIN

    def __init__(
        self,
        hass: HomeAssistant,
        coordinator: TriggerUpdateCoordinator,
        config: ConfigType,
    ) -> None:
        """Initialize."""
        TriggerEntity.__init__(self, hass, coordinator, config)
        AbstractTemplateWeather.__init__(self, config)
