"""The homee sensor platform."""

from collections.abc import Callable
from dataclasses import dataclass

from pyHomee.const import AttributeType, NodeState
from pyHomee.model import HomeeAttribute, HomeeNode

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import HomeeConfigEntry
from .const import (
    HOMEE_UNIT_TO_HA_UNIT,
    OPEN_CLOSE_MAP,
    OPEN_CLOSE_MAP_REVERSED,
    WINDOW_MAP,
    WINDOW_MAP_REVERSED,
)
from .entity import HomeeEntity, HomeeNodeEntity
from .helpers import get_name_for_enum, setup_homee_platform

PARALLEL_UPDATES = 0


def get_open_close_value(attribute: HomeeAttribute) -> str | None:
    """Return the open/close value."""
    vals = OPEN_CLOSE_MAP if not attribute.is_reversed else OPEN_CLOSE_MAP_REVERSED
    return vals.get(attribute.current_value)


def get_window_value(attribute: HomeeAttribute) -> str | None:
    """Return the states of a window open sensor."""
    vals = WINDOW_MAP if not attribute.is_reversed else WINDOW_MAP_REVERSED
    return vals.get(attribute.current_value)


def get_brightness_device_class(
    attribute: HomeeAttribute, device_class: SensorDeviceClass | None
) -> SensorDeviceClass | None:
    """Return the device class for a brightness sensor."""
    if attribute.unit == "%":
        return None
    return device_class


@dataclass(frozen=True, kw_only=True)
class HomeeSensorEntityDescription(SensorEntityDescription):
    """A class that describes Homee sensor entities."""

    device_class_fn: Callable[
        [HomeeAttribute, SensorDeviceClass | None], SensorDeviceClass | None
    ] = lambda attribute, device_class: device_class
    value_fn: Callable[[HomeeAttribute], str | float | None] = lambda value: (
        value.current_value
    )
    native_unit_of_measurement_fn: Callable[[str], str | None] = lambda homee_unit: (
        HOMEE_UNIT_TO_HA_UNIT[homee_unit]
    )


SENSOR_DESCRIPTIONS: dict[AttributeType, HomeeSensorEntityDescription] = {
    AttributeType.ACCUMULATED_ENERGY_USE: HomeeSensorEntityDescription(
        key="energy",
        device_class=SensorDeviceClass.ENERGY,
        state_class=SensorStateClass.TOTAL_INCREASING,
    ),
    AttributeType.BATTERY_LEVEL: HomeeSensorEntityDescription(
        key="battery",
        device_class=SensorDeviceClass.BATTERY,
        entity_category=EntityCategory.DIAGNOSTIC,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.BRIGHTNESS: HomeeSensorEntityDescription(
        key="brightness",
        device_class=SensorDeviceClass.ILLUMINANCE,
        device_class_fn=get_brightness_device_class,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=(
            lambda attribute: (
                attribute.current_value * 1000
                if attribute.unit == "klx"
                else attribute.current_value
            )
        ),
    ),
    AttributeType.CURRENT: HomeeSensorEntityDescription(
        key="current",
        device_class=SensorDeviceClass.CURRENT,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.CURRENT_ENERGY_USE: HomeeSensorEntityDescription(
        key="power",
        device_class=SensorDeviceClass.POWER,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.DAWN: HomeeSensorEntityDescription(
        key="dawn",
        device_class=SensorDeviceClass.ILLUMINANCE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.DEVICE_TEMPERATURE: HomeeSensorEntityDescription(
        key="device_temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.EXHAUST_MOTOR_REVS: HomeeSensorEntityDescription(
        key="exhaust_motor_revs",
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    AttributeType.EXTERNAL_TEMPERATURE: HomeeSensorEntityDescription(
        key="external_temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.FLOOR_TEMPERATURE: HomeeSensorEntityDescription(
        key="floor_temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.INDOOR_RELATIVE_HUMIDITY: HomeeSensorEntityDescription(
        key="indoor_humidity",
        device_class=SensorDeviceClass.HUMIDITY,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.INDOOR_TEMPERATURE: HomeeSensorEntityDescription(
        key="indoor_temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.INTAKE_MOTOR_REVS: HomeeSensorEntityDescription(
        key="intake_motor_revs",
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    AttributeType.LEVEL: HomeeSensorEntityDescription(
        key="level",
        device_class=SensorDeviceClass.VOLUME_STORAGE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.LINK_QUALITY: HomeeSensorEntityDescription(
        key="link_quality",
        entity_category=EntityCategory.DIAGNOSTIC,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.OPERATING_HOURS: HomeeSensorEntityDescription(
        key="operating_hours",
        device_class=SensorDeviceClass.DURATION,
        state_class=SensorStateClass.TOTAL_INCREASING,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    AttributeType.OUTDOOR_RELATIVE_HUMIDITY: HomeeSensorEntityDescription(
        key="outdoor_humidity",
        device_class=SensorDeviceClass.HUMIDITY,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.OUTDOOR_TEMPERATURE: HomeeSensorEntityDescription(
        key="outdoor_temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.POSITION: HomeeSensorEntityDescription(
        key="position",
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.RAIN_FALL_LAST_HOUR: HomeeSensorEntityDescription(
        key="rainfall_hour",
        device_class=SensorDeviceClass.PRECIPITATION,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.RAIN_FALL_TODAY: HomeeSensorEntityDescription(
        key="rainfall_day",
        device_class=SensorDeviceClass.PRECIPITATION,
        state_class=SensorStateClass.TOTAL_INCREASING,
    ),
    AttributeType.RELATIVE_HUMIDITY: HomeeSensorEntityDescription(
        key="humidity",
        device_class=SensorDeviceClass.HUMIDITY,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.TEMPERATURE: HomeeSensorEntityDescription(
        key="temperature",
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.TOTAL_ACCUMULATED_ENERGY_USE: HomeeSensorEntityDescription(
        key="total_energy",
        device_class=SensorDeviceClass.ENERGY,
        state_class=SensorStateClass.TOTAL_INCREASING,
    ),
    AttributeType.TOTAL_CURRENT: HomeeSensorEntityDescription(
        key="total_current",
        device_class=SensorDeviceClass.CURRENT,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.TOTAL_CURRENT_ENERGY_USE: HomeeSensorEntityDescription(
        key="total_power",
        device_class=SensorDeviceClass.POWER,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.TOTAL_VOLTAGE: HomeeSensorEntityDescription(
        key="total_voltage",
        device_class=SensorDeviceClass.VOLTAGE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.UP_DOWN: HomeeSensorEntityDescription(
        key="up_down",
        device_class=SensorDeviceClass.ENUM,
        options=[
            "open",
            "closed",
            "partial",
            "opening",
            "closing",
        ],
        value_fn=get_open_close_value,
    ),
    AttributeType.UV: HomeeSensorEntityDescription(
        key="uv",
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.VOLTAGE: HomeeSensorEntityDescription(
        key="voltage",
        device_class=SensorDeviceClass.VOLTAGE,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.WIND_SPEED: HomeeSensorEntityDescription(
        key="wind_speed",
        device_class=SensorDeviceClass.WIND_SPEED,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    AttributeType.WINDOW_POSITION: HomeeSensorEntityDescription(
        key="window_position",
        device_class=SensorDeviceClass.ENUM,
        options=["closed", "open", "tilted"],
        value_fn=get_window_value,
    ),
}


@dataclass(frozen=True, kw_only=True)
class HomeeNodeSensorEntityDescription(SensorEntityDescription):
    """Describes Homee node sensor entities."""

    value_fn: Callable[[HomeeNode], str | None]


NODE_SENSOR_DESCRIPTIONS: tuple[HomeeNodeSensorEntityDescription, ...] = (
    HomeeNodeSensorEntityDescription(
        key="state",
        device_class=SensorDeviceClass.ENUM,
        options=[
            "available",
            "unavailable",
            "update_in_progress",
            "waiting_for_attributes",
            "initializing",
            "user_interaction_required",
            "password_required",
            "host_unavailable",
            "delete_in_progress",
            "cosi_connected",
            "blocked",
            "waiting_for_wakeup",
            "remote_node_deleted",
            "firmware_update_in_progress",
        ],
        entity_category=EntityCategory.DIAGNOSTIC,
        entity_registry_enabled_default=False,
        translation_key="node_state",
        value_fn=lambda node: get_name_for_enum(NodeState, node.state),
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: HomeeConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Add the homee platform for the sensor components."""

    async def add_sensor_entities(
        config_entry: HomeeConfigEntry,
        async_add_entities: AddConfigEntryEntitiesCallback,
        nodes: list[HomeeNode],
    ) -> None:
        """Add homee sensor entities."""
        entities: list[HomeeSensor | HomeeNodeSensor] = []

        for node in nodes:
            # Node properties that are sensors.
            entities.extend(
                HomeeNodeSensor(node, config_entry, description)
                for description in NODE_SENSOR_DESCRIPTIONS
            )

            # Node attributes that are sensors.
            entities.extend(
                HomeeSensor(
                    attribute, config_entry, SENSOR_DESCRIPTIONS[attribute.type]
                )
                for attribute in node.attributes
                if attribute.type in SENSOR_DESCRIPTIONS and not attribute.editable
            )

        if entities:
            async_add_entities(entities)

    await setup_homee_platform(add_sensor_entities, async_add_entities, config_entry)


class HomeeSensor(HomeeEntity, SensorEntity):
    """Representation of a homee sensor."""

    entity_description: HomeeSensorEntityDescription

    def __init__(
        self,
        attribute: HomeeAttribute,
        entry: HomeeConfigEntry,
        description: HomeeSensorEntityDescription,
    ) -> None:
        """Initialize a homee sensor entity."""
        super().__init__(attribute, entry)
        self.entity_description = description
        self._attr_translation_key = description.key
        if attribute.instance > 0:
            self._attr_translation_key = f"{self._attr_translation_key}_instance"
            self._attr_translation_placeholders = {"instance": str(attribute.instance)}
        self._attr_device_class = description.device_class_fn(
            attribute, description.device_class
        )

    @property
    def native_value(self) -> float | str | None:
        """Return the native value of the sensor."""
        return self.entity_description.value_fn(self._attribute)

    @property
    def native_unit_of_measurement(self) -> str | None:
        """Return the native unit of the sensor."""
        return self.entity_description.native_unit_of_measurement_fn(
            self._attribute.unit
        )


class HomeeNodeSensor(HomeeNodeEntity, SensorEntity):
    """Represents a sensor based on a node's property."""

    entity_description: HomeeNodeSensorEntityDescription

    def __init__(
        self,
        node: HomeeNode,
        entry: HomeeConfigEntry,
        description: HomeeNodeSensorEntityDescription,
    ) -> None:
        """Initialize a homee node sensor entity."""
        super().__init__(node, entry)
        self.entity_description = description
        self._node = node
        self._attr_unique_id = f"{self._attr_unique_id}-{description.key}"

    @property
    def native_value(self) -> str | None:
        """Return the sensors value."""
        return self.entity_description.value_fn(self._node)
