"""Support for displaying weather info from Ecobee API."""

from datetime import timedelta
from typing import override

from pyecobee.const import ECOBEE_STATE_UNKNOWN

from homeassistant.components.weather import (
    ATTR_FORECAST_CONDITION,
    ATTR_FORECAST_NATIVE_TEMP,
    ATTR_FORECAST_NATIVE_TEMP_LOW,
    ATTR_FORECAST_NATIVE_WIND_SPEED,
    ATTR_FORECAST_TIME,
    ATTR_FORECAST_WIND_BEARING,
    Forecast,
    WeatherEntity,
    WeatherEntityFeature,
)
from homeassistant.const import (
    UnitOfLength,
    UnitOfPressure,
    UnitOfSpeed,
    UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util

from . import EcobeeConfigEntry, EcobeeData
from .const import (
    DOMAIN,
    ECOBEE_MODEL_TO_NAME,
    ECOBEE_WEATHER_SYMBOL_TO_HASS,
    MANUFACTURER,
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: EcobeeConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the ecobee weather platform."""
    data = config_entry.runtime_data
    dev = []
    for index in range(len(data.ecobee.thermostats)):
        thermostat = data.ecobee.get_thermostat(index)
        if "weather" in thermostat:
            dev.append(EcobeeWeather(data, thermostat["name"], index))

    async_add_entities(dev, True)


class EcobeeWeather(WeatherEntity):
    """Representation of Ecobee weather data."""

    _attr_native_pressure_unit = UnitOfPressure.HPA
    _attr_native_temperature_unit = UnitOfTemperature.FAHRENHEIT
    _attr_native_visibility_unit = UnitOfLength.METERS
    _attr_native_wind_speed_unit = UnitOfSpeed.MILES_PER_HOUR
    _attr_has_entity_name = True
    _attr_name = None
    _attr_supported_features = WeatherEntityFeature.FORECAST_DAILY

    def __init__(self, data: EcobeeData, name: str, index: int) -> None:
        """Initialize the Ecobee weather platform."""
        self.data = data
        self._name = name
        self._index = index
        self.weather = None
        self._attr_unique_id = data.ecobee.get_thermostat(self._index)["identifier"]

    def get_forecast(self, index, param):
        """Retrieve forecast parameter."""
        try:
            forecast = self.weather["forecasts"][index]
            return forecast[param]
        except (IndexError, KeyError) as err:
            raise ValueError from err

    @property
    @override
    def device_info(self) -> DeviceInfo:
        """Return device information for the ecobee weather platform."""
        thermostat = self.data.ecobee.get_thermostat(self._index)
        model: str | None
        try:
            model = f"{ECOBEE_MODEL_TO_NAME[thermostat['modelNumber']]} Thermostat"
        except KeyError:
            # Ecobee model is not in our list
            model = None

        return DeviceInfo(
            identifiers={(DOMAIN, thermostat["identifier"])},
            manufacturer=MANUFACTURER,
            model=model,
            name=self._name,
        )

    @property
    @override
    def condition(self) -> str | None:
        """Return the current condition."""
        try:
            return ECOBEE_WEATHER_SYMBOL_TO_HASS[self.get_forecast(0, "weatherSymbol")]
        except ValueError:
            return None

    @property
    @override
    def native_temperature(self) -> float | None:
        """Return the temperature."""
        try:
            return float(self.get_forecast(0, "temperature")) / 10
        except ValueError:
            return None

    @property
    @override
    def native_pressure(self) -> float | None:
        """Return the pressure."""
        try:
            pressure = self.get_forecast(0, "pressure")
            return round(pressure)
        except ValueError:
            return None

    @property
    @override
    def humidity(self) -> float | None:
        """Return the humidity."""
        try:
            return int(self.get_forecast(0, "relativeHumidity"))
        except ValueError:
            return None

    @property
    @override
    def native_visibility(self) -> float | None:
        """Return the visibility."""
        try:
            return int(self.get_forecast(0, "visibility"))
        except ValueError:
            return None

    @property
    @override
    def native_wind_speed(self) -> float | None:
        """Return the wind speed."""
        try:
            return int(self.get_forecast(0, "windSpeed"))
        except ValueError:
            return None

    @property
    @override
    def wind_bearing(self) -> float | None:
        """Return the wind direction."""
        try:
            return int(self.get_forecast(0, "windBearing"))
        except ValueError:
            return None

    @property
    @override
    def attribution(self) -> str | None:
        """Return the attribution."""
        if not self.weather:
            return None

        station = self.weather.get("weatherStation", "UNKNOWN")
        time = self.weather.get("timestamp", "UNKNOWN")
        return f"Ecobee weather provided by {station} at {time} UTC"

    def _forecast(self) -> list[Forecast] | None:
        """Return the forecast array."""
        if not self.weather or "forecasts" not in self.weather:
            return None

        forecasts: list[Forecast] = []
        date = dt_util.utcnow()
        for day in range(5):
            forecast = _process_forecast(self.weather["forecasts"][day])
            if forecast is None:
                continue
            forecast[ATTR_FORECAST_TIME] = date.isoformat()
            date += timedelta(days=1)
            forecasts.append(forecast)

        if forecasts:
            return forecasts
        return None

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

    async def async_update(self) -> None:
        """Get the latest weather data."""
        await self.data.update()
        thermostat = self.data.ecobee.get_thermostat(self._index)
        self.weather = thermostat.get("weather")
        await self.async_update_listeners(("daily",))


def _process_forecast(json):
    """Process a single ecobee API forecast to return expected values."""
    forecast = {}
    try:
        forecast[ATTR_FORECAST_CONDITION] = ECOBEE_WEATHER_SYMBOL_TO_HASS[
            json["weatherSymbol"]
        ]
        if json["tempHigh"] != ECOBEE_STATE_UNKNOWN:
            forecast[ATTR_FORECAST_NATIVE_TEMP] = float(json["tempHigh"]) / 10
        if json["tempLow"] != ECOBEE_STATE_UNKNOWN:
            forecast[ATTR_FORECAST_NATIVE_TEMP_LOW] = float(json["tempLow"]) / 10
        if json["windBearing"] != ECOBEE_STATE_UNKNOWN:
            forecast[ATTR_FORECAST_WIND_BEARING] = int(json["windBearing"])
        if json["windSpeed"] != ECOBEE_STATE_UNKNOWN:
            forecast[ATTR_FORECAST_NATIVE_WIND_SPEED] = int(json["windSpeed"])

    except ValueError, IndexError, KeyError:
        return None

    if forecast:
        return forecast
    return None
