"""Support for Samsung Printers with SyncThru web interface."""

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

from pysyncthru import SyncThru, SyncthruState

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .coordinator import SyncThruConfigEntry
from .entity import SyncthruEntity

SYNCTHRU_STATE_HUMAN = {
    SyncthruState.INVALID: "invalid",
    SyncthruState.OFFLINE: "unreachable",
    SyncthruState.NORMAL: "normal",
    SyncthruState.UNKNOWN: "unknown",
    SyncthruState.WARNING: "warning",
    SyncthruState.TESTING: "testing",
    SyncthruState.ERROR: "error",
}


@dataclass(frozen=True, kw_only=True)
class SyncThruSensorDescription(SensorEntityDescription):
    """Describes a SyncThru sensor entity."""

    value_fn: Callable[[SyncThru], str | None]
    extra_state_attributes_fn: Callable[[SyncThru], dict[str, str | int]] | None = None


def get_toner_entity_description(color: str) -> SyncThruSensorDescription:
    """Get toner entity description for a specific color."""
    return SyncThruSensorDescription(
        key=f"toner_{color}",
        translation_key=f"toner_{color}",
        entity_category=EntityCategory.DIAGNOSTIC,
        native_unit_of_measurement=PERCENTAGE,
        value_fn=lambda printer: printer.toner_status().get(color, {}).get("remaining"),
        extra_state_attributes_fn=lambda printer: printer.toner_status().get(color, {}),
    )


def get_drum_entity_description(color: str) -> SyncThruSensorDescription:
    """Get drum entity description for a specific color."""
    return SyncThruSensorDescription(
        key=f"drum_{color}",
        translation_key=f"drum_{color}",
        entity_category=EntityCategory.DIAGNOSTIC,
        native_unit_of_measurement=PERCENTAGE,
        value_fn=lambda printer: printer.drum_status().get(color, {}).get("remaining"),
        extra_state_attributes_fn=lambda printer: printer.drum_status().get(color, {}),
    )


def get_input_tray_entity_description(tray: str) -> SyncThruSensorDescription:
    """Get input tray entity description for a specific tray."""
    placeholders = {}
    translation_key = f"tray_{tray}"
    if "_" in tray:
        _, identifier = tray.split("_")
        placeholders["tray_number"] = identifier
        translation_key = "tray"
    return SyncThruSensorDescription(
        key=f"tray_{tray}",
        translation_key=translation_key,
        entity_category=EntityCategory.DIAGNOSTIC,
        translation_placeholders=placeholders,
        value_fn=(
            lambda printer: (
                printer.input_tray_status().get(tray, {}).get("newError") or "Ready"
            )
        ),
        extra_state_attributes_fn=(
            lambda printer: printer.input_tray_status().get(tray, {})
        ),
    )


def get_output_tray_entity_description(tray: int) -> SyncThruSensorDescription:
    """Get output tray entity description for a specific tray."""
    return SyncThruSensorDescription(
        key=f"output_tray_{tray}",
        translation_key="output_tray",
        entity_category=EntityCategory.DIAGNOSTIC,
        translation_placeholders={"tray_number": str(tray)},
        value_fn=(
            lambda printer: (
                printer.output_tray_status().get(tray, {}).get("status") or "Ready"
            )
        ),
        extra_state_attributes_fn=(
            lambda printer: cast(
                dict[str, str | int], printer.output_tray_status().get(tray, {})
            )
        ),
    )


SENSOR_TYPES: tuple[SyncThruSensorDescription, ...] = (
    SyncThruSensorDescription(
        key="active_alerts",
        translation_key="active_alerts",
        value_fn=lambda printer: printer.raw().get("GXI_ACTIVE_ALERT_TOTAL"),
    ),
    SyncThruSensorDescription(
        key="main",
        name=None,
        value_fn=lambda printer: SYNCTHRU_STATE_HUMAN[printer.device_status()],
        extra_state_attributes_fn=lambda printer: {
            "display_text": printer.device_status_details(),
        },
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: SyncThruConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up from config entry."""

    coordinator = config_entry.runtime_data
    printer = coordinator.data

    supp_toner = printer.toner_status(filter_supported=True)
    supp_drum = printer.drum_status(filter_supported=True)
    supp_tray = printer.input_tray_status(filter_supported=True)
    supp_output_tray = printer.output_tray_status()

    entities: list[SyncThruSensorDescription] = [
        get_toner_entity_description(color) for color in supp_toner
    ]
    entities.extend(get_drum_entity_description(color) for color in supp_drum)
    entities.extend(get_input_tray_entity_description(key) for key in supp_tray)
    entities.extend(get_output_tray_entity_description(key) for key in supp_output_tray)

    async_add_entities(
        SyncThruSensor(coordinator, description)
        for description in SENSOR_TYPES + tuple(entities)
    )


class SyncThruSensor(SyncthruEntity, SensorEntity):
    """Implementation of an abstract Samsung Printer sensor platform."""

    _attr_icon = "mdi:printer"
    entity_description: SyncThruSensorDescription

    @property
    @override
    def native_value(self) -> str | int | None:
        """Return the state of the sensor."""
        return self.entity_description.value_fn(self.coordinator.data)

    @property
    @override
    def extra_state_attributes(self) -> dict[str, Any] | None:
        """Return the state attributes."""
        if self.entity_description.extra_state_attributes_fn:
            return self.entity_description.extra_state_attributes_fn(
                self.coordinator.data
            )
        return None
