"""Support for switch entities."""
from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime, timedelta

from gardena_bluetooth.const import Battery, Sensor, Valve
from gardena_bluetooth.parse import Characteristic

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.util.dt as dt_util

from .const import DOMAIN
from .coordinator import (
    Coordinator,
    GardenaBluetoothDescriptorEntity,
    GardenaBluetoothEntity,
)


@dataclass
class GardenaBluetoothSensorEntityDescription(SensorEntityDescription):
    """Description of entity."""

    char: Characteristic = field(default_factory=lambda: Characteristic(""))
    connected_state: Characteristic | None = None

    @property
    def context(self) -> set[str]:
        """Context needed for update coordinator."""
        data = {self.char.uuid}
        if self.connected_state:
            data.add(self.connected_state.uuid)
        return data


DESCRIPTIONS = (
    GardenaBluetoothSensorEntityDescription(
        key=Valve.activation_reason.uuid,
        translation_key="activation_reason",
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        entity_registry_enabled_default=False,
        char=Valve.activation_reason,
    ),
    GardenaBluetoothSensorEntityDescription(
        key=Battery.battery_level.uuid,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.BATTERY,
        entity_category=EntityCategory.DIAGNOSTIC,
        native_unit_of_measurement=PERCENTAGE,
        char=Battery.battery_level,
    ),
    GardenaBluetoothSensorEntityDescription(
        key=Sensor.battery_level.uuid,
        translation_key="sensor_battery_level",
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.BATTERY,
        entity_category=EntityCategory.DIAGNOSTIC,
        native_unit_of_measurement=PERCENTAGE,
        char=Sensor.battery_level,
        connected_state=Sensor.connected_state,
    ),
    GardenaBluetoothSensorEntityDescription(
        key=Sensor.value.uuid,
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.MOISTURE,
        native_unit_of_measurement=PERCENTAGE,
        char=Sensor.value,
        connected_state=Sensor.connected_state,
    ),
    GardenaBluetoothSensorEntityDescription(
        key=Sensor.type.uuid,
        translation_key="sensor_type",
        entity_category=EntityCategory.DIAGNOSTIC,
        char=Sensor.type,
        connected_state=Sensor.connected_state,
    ),
    GardenaBluetoothSensorEntityDescription(
        key=Sensor.measurement_timestamp.uuid,
        translation_key="sensor_measurement_timestamp",
        state_class=SensorStateClass.MEASUREMENT,
        device_class=SensorDeviceClass.TIMESTAMP,
        entity_category=EntityCategory.DIAGNOSTIC,
        char=Sensor.measurement_timestamp,
        connected_state=Sensor.connected_state,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up Gardena Bluetooth sensor based on a config entry."""
    coordinator: Coordinator = hass.data[DOMAIN][entry.entry_id]
    entities: list[GardenaBluetoothEntity] = [
        GardenaBluetoothSensor(coordinator, description, description.context)
        for description in DESCRIPTIONS
        if description.key in coordinator.characteristics
    ]
    if Valve.remaining_open_time.uuid in coordinator.characteristics:
        entities.append(GardenaBluetoothRemainSensor(coordinator))
    async_add_entities(entities)


class GardenaBluetoothSensor(GardenaBluetoothDescriptorEntity, SensorEntity):
    """Representation of a sensor."""

    entity_description: GardenaBluetoothSensorEntityDescription

    def _handle_coordinator_update(self) -> None:
        value = self.coordinator.get_cached(self.entity_description.char)
        if isinstance(value, datetime):
            value = value.replace(
                tzinfo=dt_util.get_time_zone(self.hass.config.time_zone)
            )
        self._attr_native_value = value

        if char := self.entity_description.connected_state:
            self._attr_available = bool(self.coordinator.get_cached(char))
        else:
            self._attr_available = True

        super()._handle_coordinator_update()


class GardenaBluetoothRemainSensor(GardenaBluetoothEntity, SensorEntity):
    """Representation of a sensor."""

    _attr_device_class = SensorDeviceClass.TIMESTAMP
    _attr_native_value: datetime | None = None
    _attr_translation_key = "remaining_open_timestamp"

    def __init__(
        self,
        coordinator: Coordinator,
    ) -> None:
        """Initialize the sensor."""
        super().__init__(coordinator, {Valve.remaining_open_time.uuid})
        self._attr_unique_id = f"{coordinator.address}-remaining_open_timestamp"

    def _handle_coordinator_update(self) -> None:
        value = self.coordinator.get_cached(Valve.remaining_open_time)
        if not value:
            self._attr_native_value = None
            super()._handle_coordinator_update()
            return

        time = datetime.now(UTC) + timedelta(seconds=value)
        if not self._attr_native_value:
            self._attr_native_value = time
            super()._handle_coordinator_update()
            return

        error = time - self._attr_native_value
        if abs(error.total_seconds()) > 10:
            self._attr_native_value = time
            super()._handle_coordinator_update()
            return

    @property
    def available(self) -> bool:
        """Sensor only available when open."""
        return super().available and self._attr_native_value is not None
