"""Sensor platform for IronOS integration."""

from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import StrEnum
from typing import override

from pynecil import LiveDataResponse, OperatingMode, PowerSource

from homeassistant.components.sensor import (
    EntityCategory,
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.const import (
    PERCENTAGE,
    UnitOfElectricPotential,
    UnitOfPower,
    UnitOfTemperature,
    UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util

from . import IronOSConfigEntry
from .const import OHM
from .coordinator import IronOSLiveDataCoordinator
from .entity import IronOSBaseEntity

# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0


class PinecilSensor(StrEnum):
    """Pinecil Sensors."""

    LIVE_TEMP = "live_temperature"
    SETPOINT_TEMP = "setpoint_temperature"
    DC_VOLTAGE = "voltage"
    HANDLETEMP = "handle_temperature"
    PWMLEVEL = "power_pwm_level"
    POWER_SRC = "power_source"
    TIP_RESISTANCE = "tip_resistance"
    UPTIME = "uptime"
    MOVEMENT_TIME = "movement_time"
    MAX_TIP_TEMP_ABILITY = "max_tip_temp_ability"
    TIP_VOLTAGE = "tip_voltage"
    HALL_SENSOR = "hall_sensor"
    OPERATING_MODE = "operating_mode"
    ESTIMATED_POWER = "estimated_power"


@dataclass(frozen=True, kw_only=True)
class IronOSSensorEntityDescription(SensorEntityDescription):
    """IronOS sensor entity descriptions."""

    value_fn: Callable[[LiveDataResponse, bool], StateType | datetime]


PINECIL_SENSOR_DESCRIPTIONS: tuple[IronOSSensorEntityDescription, ...] = (
    IronOSSensorEntityDescription(
        key=PinecilSensor.LIVE_TEMP,
        translation_key=PinecilSensor.LIVE_TEMP,
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, has_tip: data.live_temp if has_tip else None,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.DC_VOLTAGE,
        translation_key=PinecilSensor.DC_VOLTAGE,
        native_unit_of_measurement=UnitOfElectricPotential.VOLT,
        device_class=SensorDeviceClass.VOLTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, _: data.dc_voltage,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.HANDLETEMP,
        translation_key=PinecilSensor.HANDLETEMP,
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        device_class=SensorDeviceClass.TEMPERATURE,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, _: data.handle_temp,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.PWMLEVEL,
        translation_key=PinecilSensor.PWMLEVEL,
        native_unit_of_measurement=PERCENTAGE,
        suggested_display_precision=0,
        device_class=SensorDeviceClass.POWER_FACTOR,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, _: data.pwm_level,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.POWER_SRC,
        translation_key=PinecilSensor.POWER_SRC,
        device_class=SensorDeviceClass.ENUM,
        options=[item.name.lower() for item in PowerSource],
        value_fn=(
            lambda data, _: data.power_src.name.lower() if data.power_src else None
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.TIP_RESISTANCE,
        translation_key=PinecilSensor.TIP_RESISTANCE,
        native_unit_of_measurement=OHM,
        value_fn=lambda data, has_tip: data.tip_resistance if has_tip else None,
        entity_category=EntityCategory.DIAGNOSTIC,
        state_class=SensorStateClass.MEASUREMENT,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.UPTIME,
        translation_key=PinecilSensor.UPTIME,
        device_class=SensorDeviceClass.UPTIME,
        value_fn=(
            lambda data, _: (
                (dt_util.utcnow() - timedelta(seconds=data.uptime))
                if data.uptime is not None
                else None
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.MOVEMENT_TIME,
        translation_key=PinecilSensor.MOVEMENT_TIME,
        native_unit_of_measurement=UnitOfTime.SECONDS,
        device_class=SensorDeviceClass.DURATION,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, _: data.movement_time,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.MAX_TIP_TEMP_ABILITY,
        translation_key=PinecilSensor.MAX_TIP_TEMP_ABILITY,
        native_unit_of_measurement=UnitOfTemperature.CELSIUS,
        device_class=SensorDeviceClass.TEMPERATURE,
        value_fn=lambda data, has_tip: data.max_tip_temp_ability if has_tip else None,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.TIP_VOLTAGE,
        translation_key=PinecilSensor.TIP_VOLTAGE,
        native_unit_of_measurement=UnitOfElectricPotential.MICROVOLT,
        device_class=SensorDeviceClass.VOLTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        suggested_display_precision=0,
        value_fn=lambda data, has_tip: data.tip_voltage if has_tip else None,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.HALL_SENSOR,
        translation_key=PinecilSensor.HALL_SENSOR,
        state_class=SensorStateClass.MEASUREMENT,
        entity_registry_enabled_default=False,
        value_fn=lambda data, _: data.hall_sensor,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.OPERATING_MODE,
        translation_key=PinecilSensor.OPERATING_MODE,
        device_class=SensorDeviceClass.ENUM,
        options=[item.name.lower() for item in OperatingMode],
        value_fn=(
            lambda data, _: (
                data.operating_mode.name.lower() if data.operating_mode else None
            )
        ),
    ),
    IronOSSensorEntityDescription(
        key=PinecilSensor.ESTIMATED_POWER,
        translation_key=PinecilSensor.ESTIMATED_POWER,
        native_unit_of_measurement=UnitOfPower.WATT,
        device_class=SensorDeviceClass.POWER,
        state_class=SensorStateClass.MEASUREMENT,
        value_fn=lambda data, _: data.estimated_power,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: IronOSConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up sensors from a config entry."""
    coordinator = entry.runtime_data.live_data

    async_add_entities(
        IronOSSensorEntity(coordinator, description)
        for description in PINECIL_SENSOR_DESCRIPTIONS
    )


class IronOSSensorEntity(IronOSBaseEntity, SensorEntity):
    """Representation of a IronOS sensor entity."""

    entity_description: IronOSSensorEntityDescription
    coordinator: IronOSLiveDataCoordinator

    @property
    @override
    def native_value(self) -> StateType | datetime:
        """Return sensor state."""
        return self.entity_description.value_fn(
            self.coordinator.data, self.coordinator.has_tip
        )
