"""Platform for sensor integration."""

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, override

from switchbot_api import Device, Remote, SwitchBotAPI

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.const import (
    UnitOfElectricCurrent,
    UnitOfElectricPotential,
    UnitOfEnergy,
    UnitOfPower,
    UnitOfRatio,
    UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import SwitchbotCloudConfigEntry
from .const import DOMAIN, SwitchbotCloudDeviceLockState
from .coordinator import SwitchBotCoordinator
from .entity import SwitchBotCloudEntity

SENSOR_TYPE_TEMPERATURE = "temperature"
SENSOR_TYPE_HUMIDITY = "humidity"
SENSOR_TYPE_BATTERY = "battery"
SENSOR_TYPE_CO2 = "CO2"
SENSOR_TYPE_POWER = "power"
SENSOR_TYPE_VOLTAGE = "voltage"
SENSOR_TYPE_CURRENT = "electricCurrent"
SENSOR_TYPE_POWER_CONSUMPTION = "weight"
SENSOR_TYPE_USED_ELECTRICITY = "usedElectricity"
SENSOR_TYPE_LIGHTLEVEL = "lightLevel"


RELAY_SWITCH_2PM_SENSOR_TYPE_POWER = "Power"
RELAY_SWITCH_2PM_SENSOR_TYPE_VOLTAGE = "Voltage"
RELAY_SWITCH_2PM_SENSOR_TYPE_CURRENT = "ElectricCurrent"
RELAY_SWITCH_2PM_SENSOR_TYPE_ELECTRICITY = "UsedElectricity"

LOCK_SENSOR_TYPE_LOCK_STATE = "lockState"


@dataclass(frozen=True, kw_only=True)
class SwitchbotCloudSensorEntityDescription(SensorEntityDescription):
    """Plug Mini Eu UsedElectricity Sensor EntityDescription."""

    value_fn: Callable[[Any], Any] = lambda value: value


USED_ELECTRICITY_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_USED_ELECTRICITY,
    device_class=SensorDeviceClass.ENERGY,
    state_class=SensorStateClass.TOTAL_INCREASING,
    native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
    suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
    suggested_display_precision=3,
    value_fn=lambda value: (value or 0) / 60,
)

TEMPERATURE_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_TEMPERATURE,
    device_class=SensorDeviceClass.TEMPERATURE,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfTemperature.CELSIUS,
)

HUMIDITY_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_HUMIDITY,
    device_class=SensorDeviceClass.HUMIDITY,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
)

BATTERY_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_BATTERY,
    device_class=SensorDeviceClass.BATTERY,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
)

POWER_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_POWER,
    device_class=SensorDeviceClass.POWER,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfPower.WATT,
)

VOLTAGE_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_VOLTAGE,
    device_class=SensorDeviceClass.VOLTAGE,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfElectricPotential.VOLT,
)

CURRENT_DESCRIPTION_IN_MA = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_CURRENT,
    device_class=SensorDeviceClass.CURRENT,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
)

CURRENT_DESCRIPTION_IN_A = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_CURRENT,
    device_class=SensorDeviceClass.CURRENT,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
)

CO2_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_CO2,
    device_class=SensorDeviceClass.CO2,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
)

POWER_CONSUMPTION_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=SENSOR_TYPE_POWER_CONSUMPTION,
    device_class=SensorDeviceClass.POWER,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfPower.WATT,
)

RELAY_SWITCH_2PM_POWER_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=RELAY_SWITCH_2PM_SENSOR_TYPE_POWER,
    device_class=SensorDeviceClass.POWER,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfPower.WATT,
)

RELAY_SWITCH_2PM_VOLTAGE_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=RELAY_SWITCH_2PM_SENSOR_TYPE_VOLTAGE,
    device_class=SensorDeviceClass.VOLTAGE,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfElectricPotential.VOLT,
)

RELAY_SWITCH_2PM_CURRENT_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=RELAY_SWITCH_2PM_SENSOR_TYPE_CURRENT,
    device_class=SensorDeviceClass.CURRENT,
    state_class=SensorStateClass.MEASUREMENT,
    native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
)

RELAY_SWITCH_2PM_ELECTRICITY_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=RELAY_SWITCH_2PM_SENSOR_TYPE_ELECTRICITY,
    device_class=SensorDeviceClass.ENERGY,
    state_class=SensorStateClass.TOTAL_INCREASING,
    native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
    suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
    suggested_display_precision=3,
    value_fn=lambda value: (value or 0) / 60,
)

LIGHTLEVEL_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key="lightLevel",
    translation_key="light_level",
    state_class=SensorStateClass.MEASUREMENT,
)


LOCK_SENSOR_TYPE_LOCK_STATE_DESCRIPTION = SwitchbotCloudSensorEntityDescription(
    key=LOCK_SENSOR_TYPE_LOCK_STATE,
    device_class=SensorDeviceClass.ENUM,
    translation_key="lock_state",
    options=[
        value.name.lower() for value in SwitchbotCloudDeviceLockState.get_states()
    ],
    value_fn=lambda value: (
        SwitchbotCloudDeviceLockState(value).name.lower()
        if value in SwitchbotCloudDeviceLockState.get_values()
        else None
    ),
)

SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
    "Bot": (BATTERY_DESCRIPTION,),
    "Battery Circulator Fan": (BATTERY_DESCRIPTION,),
    "Standing Fan": (BATTERY_DESCRIPTION,),
    "Battery Circulator Fan 2 Pro": (BATTERY_DESCRIPTION,),
    "Meter": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
    ),
    "MeterPlus": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
    ),
    "WoIOSensor": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
    ),
    "Presence Sensor": (BATTERY_DESCRIPTION, LIGHTLEVEL_DESCRIPTION),
    "Relay Switch 1PM": (
        POWER_DESCRIPTION,
        VOLTAGE_DESCRIPTION,
        CURRENT_DESCRIPTION_IN_MA,
    ),
    "Plug Mini (US)": (
        VOLTAGE_DESCRIPTION,
        CURRENT_DESCRIPTION_IN_MA,
        POWER_CONSUMPTION_DESCRIPTION,
    ),
    "Plug Mini (JP)": (
        VOLTAGE_DESCRIPTION,
        CURRENT_DESCRIPTION_IN_MA,
        POWER_CONSUMPTION_DESCRIPTION,
    ),
    "Plug Mini (EU)": (
        POWER_DESCRIPTION,
        VOLTAGE_DESCRIPTION,
        CURRENT_DESCRIPTION_IN_MA,
        USED_ELECTRICITY_DESCRIPTION,
    ),
    "Hub 2": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
    ),
    "MeterPro": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
    ),
    "MeterPro(CO2)": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
        CO2_DESCRIPTION,
    ),
    "Smart Lock": (BATTERY_DESCRIPTION,),
    "Smart Lock Lite": (BATTERY_DESCRIPTION,),
    "Smart Lock Pro": (BATTERY_DESCRIPTION,),
    "Smart Lock Ultra": (
        BATTERY_DESCRIPTION,
        LOCK_SENSOR_TYPE_LOCK_STATE_DESCRIPTION,
    ),
    "Smart Lock Vision": (BATTERY_DESCRIPTION,),
    "Smart Lock Vision Pro": (BATTERY_DESCRIPTION,),
    "Lock Vision": (BATTERY_DESCRIPTION,),
    "Lock Vision Pro": (BATTERY_DESCRIPTION,),
    "Smart Lock Pro Wifi": (BATTERY_DESCRIPTION,),
    "Relay Switch 2PM": (
        RELAY_SWITCH_2PM_POWER_DESCRIPTION,
        RELAY_SWITCH_2PM_VOLTAGE_DESCRIPTION,
        RELAY_SWITCH_2PM_CURRENT_DESCRIPTION,
        RELAY_SWITCH_2PM_ELECTRICITY_DESCRIPTION,
    ),
    "Curtain": (BATTERY_DESCRIPTION,),
    "Curtain3": (BATTERY_DESCRIPTION,),
    "Roller Shade": (BATTERY_DESCRIPTION,),
    "Blind Tilt": (BATTERY_DESCRIPTION,),
    "Hub 3": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        LIGHTLEVEL_DESCRIPTION,
    ),
    "Motion Sensor": (BATTERY_DESCRIPTION,),
    "Contact Sensor": (BATTERY_DESCRIPTION,),
    "Water Detector": (BATTERY_DESCRIPTION,),
    "Humidifier": (TEMPERATURE_DESCRIPTION,),
    "Home Climate Panel": (
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
        BATTERY_DESCRIPTION,
    ),
    "Smart Radiator Thermostat": (BATTERY_DESCRIPTION,),
    "AI Art Frame": (BATTERY_DESCRIPTION,),
    "WeatherStation": (
        BATTERY_DESCRIPTION,
        TEMPERATURE_DESCRIPTION,
        HUMIDITY_DESCRIPTION,
    ),
}


async def async_setup_entry(
    hass: HomeAssistant,
    config: SwitchbotCloudConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up SwitchBot Cloud entry."""
    data = config.runtime_data
    entities: list[SwitchBotCloudSensor] = []
    for device, coordinator in data.devices.sensors:
        if device.device_type not in SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES:
            continue
        descriptions = SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[device.device_type]
        for description in descriptions:
            if device.device_type == "Relay Switch 2PM":
                entities.append(
                    SwitchBotCloudRelaySwitch2PMSensor(
                        data.api, device, coordinator, description, "1"
                    )
                )
                entities.append(
                    SwitchBotCloudRelaySwitch2PMSensor(
                        data.api, device, coordinator, description, "2"
                    )
                )
            else:
                entities.append(
                    _async_make_entity(data.api, device, coordinator, description)
                )
    async_add_entities(entities)


class SwitchBotCloudSensor(SwitchBotCloudEntity, SensorEntity):
    """Representation of a SwitchBot Cloud sensor entity."""

    entity_description: SwitchbotCloudSensorEntityDescription

    def __init__(
        self,
        api: SwitchBotAPI,
        device: Device,
        coordinator: SwitchBotCoordinator,
        description: SwitchbotCloudSensorEntityDescription,
    ) -> None:
        """Initialize SwitchBot Cloud sensor entity."""
        super().__init__(api, device, coordinator)
        self.entity_description = description
        self._attr_unique_id = f"{device.device_id}_{description.key}"

    @override
    def _set_attributes(self) -> None:
        """Set attributes from coordinator data."""
        if not self.coordinator.data:
            return
        value = self.coordinator.data.get(self.entity_description.key)
        self._attr_native_value = self.entity_description.value_fn(value)


class SwitchBotCloudRelaySwitch2PMSensor(SwitchBotCloudSensor):
    """Representation of a SwitchBot Cloud Relay Switch 2PM sensor entity."""

    def __init__(
        self,
        api: SwitchBotAPI,
        device: Device,
        coordinator: SwitchBotCoordinator,
        description: SwitchbotCloudSensorEntityDescription,
        channel: str,
    ) -> None:
        """Initialize SwitchBot Cloud sensor entity."""
        super().__init__(api, device, coordinator, description)

        self.entity_description = description
        self._channel = channel
        self._attr_unique_id = f"{device.device_id}-{description.key}-{channel}"
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, f"{device.device_name}-channel-{channel}")},
            manufacturer="SwitchBot",
            model=device.device_type,
            model_id="RelaySwitch2PM",
            name=f"{device.device_name} Channel {channel}",
        )

    @override
    def _set_attributes(self) -> None:
        """Set attributes from coordinator data."""
        if not self.coordinator.data:
            return
        value = self.coordinator.data.get(
            f"switch{self._channel}{self.entity_description.key.strip()}"
        )
        self._attr_native_value = self.entity_description.value_fn(value)


@callback
def _async_make_entity(
    api: SwitchBotAPI,
    device: Device | Remote,
    coordinator: SwitchBotCoordinator,
    description: SwitchbotCloudSensorEntityDescription,
) -> SwitchBotCloudSensor:
    """Make a SwitchBotCloudSensor or SwitchBotCloudRelaySwitch2PMSensor."""
    return SwitchBotCloudSensor(api, device, coordinator, description)
