"""Sensor platform for La Marzocco espresso machines."""

from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime
from typing import cast, override

from pylamarzocco.const import BackFlushStatus, MachineState, ModelName, WidgetType
from pylamarzocco.models import (
    BackFlush,
    BaseWidgetOutput,
    CoffeeAndFlushCounter,
    CoffeeBoiler,
    MachineStatus,
    SteamBoilerLevel,
    SteamBoilerTemperature,
)

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 homeassistant.helpers.typing import StateType

from .coordinator import LaMarzoccoConfigEntry
from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription

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


@dataclass(frozen=True, kw_only=True)
class LaMarzoccoSensorEntityDescription(
    LaMarzoccoEntityDescription,
    SensorEntityDescription,
):
    """Description of a La Marzocco sensor."""

    value_fn: Callable[
        [dict[WidgetType, BaseWidgetOutput]], StateType | datetime | None
    ]


ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = (
    LaMarzoccoSensorEntityDescription(
        key="coffee_boiler_ready_time",
        translation_key="coffee_boiler_ready_time",
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=(
            lambda config: (
                cast(CoffeeBoiler, config[WidgetType.CM_COFFEE_BOILER]).ready_start_time
            )
        ),
        available_fn=(
            lambda coordinator: (
                cast(
                    CoffeeBoiler,
                    coordinator.device.dashboard.config[WidgetType.CM_COFFEE_BOILER],
                ).ready_start_time
                is not None
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    LaMarzoccoSensorEntityDescription(
        key="steam_boiler_ready_time",
        translation_key="steam_boiler_ready_time",
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=(
            lambda config: (
                cast(
                    SteamBoilerLevel, config[WidgetType.CM_STEAM_BOILER_LEVEL]
                ).ready_start_time
            )
        ),
        supported_fn=(
            lambda coordinator: (
                coordinator.device.dashboard.model_name
                in (ModelName.LINEA_MICRA, ModelName.LINEA_MINI_R)
            )
        ),
        available_fn=(
            lambda coordinator: (
                cast(
                    SteamBoilerLevel,
                    coordinator.device.dashboard.config[
                        WidgetType.CM_STEAM_BOILER_LEVEL
                    ],
                ).ready_start_time
                is not None
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    LaMarzoccoSensorEntityDescription(
        key="brewing_start_time",
        translation_key="brewing_start_time",
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=(
            lambda config: (
                cast(
                    MachineStatus, config[WidgetType.CM_MACHINE_STATUS]
                ).brewing_start_time
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
        available_fn=(
            lambda coordinator: (
                not coordinator.websocket_terminated
                and cast(
                    MachineStatus,
                    coordinator.device.dashboard.config[WidgetType.CM_MACHINE_STATUS],
                ).status
                is MachineState.BREWING
            )
        ),
    ),
    LaMarzoccoSensorEntityDescription(
        key="steam_boiler_ready_time",
        translation_key="steam_boiler_ready_time",
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=(
            lambda config: (
                cast(
                    SteamBoilerTemperature,
                    config[WidgetType.CM_STEAM_BOILER_TEMPERATURE],
                ).ready_start_time
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
        supported_fn=(
            lambda coordinator: (
                coordinator.device.dashboard.model_name
                in (ModelName.GS3_AV, ModelName.GS3_MP, ModelName.LINEA_MINI)
            )
        ),
    ),
    LaMarzoccoSensorEntityDescription(
        key="last_cleaning_time",
        translation_key="last_cleaning_time",
        device_class=SensorDeviceClass.TIMESTAMP,
        value_fn=(
            lambda config: (
                cast(
                    BackFlush,
                    config.get(
                        WidgetType.CM_BACK_FLUSH, BackFlush(status=BackFlushStatus.OFF)
                    ),
                ).last_cleaning_start_time
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
        supported_fn=(
            lambda coordinator: (
                coordinator.device.dashboard.model_name is not ModelName.GS3_MP
            )
        ),
    ),
)

STATISTIC_ENTITIES: tuple[LaMarzoccoSensorEntityDescription, ...] = (
    LaMarzoccoSensorEntityDescription(
        key="drink_stats_coffee",
        translation_key="total_coffees_made",
        state_class=SensorStateClass.TOTAL_INCREASING,
        value_fn=(
            lambda statistics: (
                cast(
                    CoffeeAndFlushCounter,
                    statistics[WidgetType.COFFEE_AND_FLUSH_COUNTER],
                ).total_coffee
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
    LaMarzoccoSensorEntityDescription(
        key="drink_stats_flushing",
        translation_key="total_flushes_done",
        state_class=SensorStateClass.TOTAL_INCREASING,
        value_fn=(
            lambda statistics: (
                cast(
                    CoffeeAndFlushCounter,
                    statistics[WidgetType.COFFEE_AND_FLUSH_COUNTER],
                ).total_flush
            )
        ),
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: LaMarzoccoConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up sensor entities."""
    config_coordinator = entry.runtime_data.config_coordinator
    statistic_coordinators = entry.runtime_data.statistics_coordinator

    entities = [
        LaMarzoccoSensorEntity(config_coordinator, description)
        for description in ENTITIES
        if description.supported_fn(config_coordinator)
    ]
    entities.extend(
        LaMarzoccoStatisticSensorEntity(statistic_coordinators, description)
        for description in STATISTIC_ENTITIES
        if description.supported_fn(statistic_coordinators)
    )
    async_add_entities(entities)


class LaMarzoccoSensorEntity(LaMarzoccoEntity, SensorEntity):
    """Sensor for La Marzocco."""

    entity_description: LaMarzoccoSensorEntityDescription

    @property
    @override
    def native_value(self) -> StateType | datetime | None:
        """Return  value of the sensor."""
        return self.entity_description.value_fn(
            self.coordinator.device.dashboard.config
        )


class LaMarzoccoStatisticSensorEntity(LaMarzoccoSensorEntity):
    """Sensor for La Marzocco statistics."""

    _unavailable_when_machine_off = False

    @property
    @override
    def native_value(self) -> StateType | datetime | None:
        """Return the value of the sensor."""
        return self.entity_description.value_fn(
            self.coordinator.device.statistics.widgets
        )
