"""Support for the Brother service."""
from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime
import logging

from brother import BrotherSensors

from homeassistant.components.sensor import (
    DOMAIN as PLATFORM,
    SensorDeviceClass,
    SensorEntity,
    SensorEntityDescription,
    SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, PERCENTAGE
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import BrotherDataUpdateCoordinator
from .const import DATA_CONFIG_ENTRY, DOMAIN

ATTR_COUNTER = "counter"
ATTR_REMAINING_PAGES = "remaining_pages"

UNIT_PAGES = "p"

_LOGGER = logging.getLogger(__name__)


@dataclass
class BrotherSensorRequiredKeysMixin:
    """Class for Brother entity required keys."""

    value: Callable[[BrotherSensors], StateType | datetime]


@dataclass
class BrotherSensorEntityDescription(
    SensorEntityDescription, BrotherSensorRequiredKeysMixin
):
    """A class that describes sensor entities."""


SENSOR_TYPES: tuple[BrotherSensorEntityDescription, ...] = (
    BrotherSensorEntityDescription(
        key="status",
        icon="mdi:printer",
        name="Status",
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.status,
    ),
    BrotherSensorEntityDescription(
        key="page_counter",
        icon="mdi:file-document-outline",
        name="Page counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.page_counter,
    ),
    BrotherSensorEntityDescription(
        key="bw_counter",
        icon="mdi:file-document-outline",
        name="B/W counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.bw_counter,
    ),
    BrotherSensorEntityDescription(
        key="color_counter",
        icon="mdi:file-document-outline",
        name="Color counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.color_counter,
    ),
    BrotherSensorEntityDescription(
        key="duplex_unit_pages_counter",
        icon="mdi:file-document-outline",
        name="Duplex unit pages counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.duplex_unit_pages_counter,
    ),
    BrotherSensorEntityDescription(
        key="drum_remaining_life",
        icon="mdi:chart-donut",
        name="Drum remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.drum_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="drum_remaining_pages",
        icon="mdi:chart-donut",
        name="Drum remaining pages",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.drum_remaining_pages,
    ),
    BrotherSensorEntityDescription(
        key="drum_counter",
        icon="mdi:chart-donut",
        name="Drum counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.drum_counter,
    ),
    BrotherSensorEntityDescription(
        key="black_drum_remaining_life",
        icon="mdi:chart-donut",
        name="Black drum remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.black_drum_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="black_drum_remaining_pages",
        icon="mdi:chart-donut",
        name="Black drum remaining pages",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.black_drum_remaining_pages,
    ),
    BrotherSensorEntityDescription(
        key="black_drum_counter",
        icon="mdi:chart-donut",
        name="Black drum counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.black_drum_counter,
    ),
    BrotherSensorEntityDescription(
        key="cyan_drum_remaining_life",
        icon="mdi:chart-donut",
        name="Cyan drum remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.cyan_drum_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="cyan_drum_remaining_pages",
        icon="mdi:chart-donut",
        name="Cyan drum remaining pages",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.cyan_drum_remaining_pages,
    ),
    BrotherSensorEntityDescription(
        key="cyan_drum_counter",
        icon="mdi:chart-donut",
        name="Cyan drum counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.cyan_drum_counter,
    ),
    BrotherSensorEntityDescription(
        key="magenta_drum_remaining_life",
        icon="mdi:chart-donut",
        name="Magenta drum remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.magenta_drum_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="magenta_drum_remaining_pages",
        icon="mdi:chart-donut",
        name="Magenta drum remaining pages",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.magenta_drum_remaining_pages,
    ),
    BrotherSensorEntityDescription(
        key="magenta_drum_counter",
        icon="mdi:chart-donut",
        name="Magenta drum counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.magenta_drum_counter,
    ),
    BrotherSensorEntityDescription(
        key="yellow_drum_remaining_life",
        icon="mdi:chart-donut",
        name="Yellow drum remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.yellow_drum_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="yellow_drum_remaining_pages",
        icon="mdi:chart-donut",
        name="Yellow drum remaining pages",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.yellow_drum_remaining_pages,
    ),
    BrotherSensorEntityDescription(
        key="yellow_drum_counter",
        icon="mdi:chart-donut",
        name="Yellow drum counter",
        native_unit_of_measurement=UNIT_PAGES,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.yellow_drum_counter,
    ),
    BrotherSensorEntityDescription(
        key="belt_unit_remaining_life",
        icon="mdi:current-ac",
        name="Belt unit remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.belt_unit_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="fuser_remaining_life",
        icon="mdi:water-outline",
        name="Fuser remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.fuser_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="laser_remaining_life",
        icon="mdi:spotlight-beam",
        name="Laser remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.laser_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="pf_kit_1_remaining_life",
        icon="mdi:printer-3d",
        name="PF Kit 1 remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.pf_kit_1_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="pf_kit_mp_remaining_life",
        icon="mdi:printer-3d",
        name="PF Kit MP remaining life",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.pf_kit_mp_remaining_life,
    ),
    BrotherSensorEntityDescription(
        key="black_toner_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Black toner remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.black_toner_remaining,
    ),
    BrotherSensorEntityDescription(
        key="cyan_toner_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Cyan toner remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.cyan_toner_remaining,
    ),
    BrotherSensorEntityDescription(
        key="magenta_toner_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Magenta toner remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.magenta_toner_remaining,
    ),
    BrotherSensorEntityDescription(
        key="yellow_toner_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Yellow toner remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.yellow_toner_remaining,
    ),
    BrotherSensorEntityDescription(
        key="black_ink_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Black ink remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.black_ink_remaining,
    ),
    BrotherSensorEntityDescription(
        key="cyan_ink_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Cyan ink remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.cyan_ink_remaining,
    ),
    BrotherSensorEntityDescription(
        key="magenta_ink_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Magenta ink remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.magenta_ink_remaining,
    ),
    BrotherSensorEntityDescription(
        key="yellow_ink_remaining",
        icon="mdi:printer-3d-nozzle",
        name="Yellow ink remaining",
        native_unit_of_measurement=PERCENTAGE,
        state_class=SensorStateClass.MEASUREMENT,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.yellow_ink_remaining,
    ),
    BrotherSensorEntityDescription(
        key="uptime",
        name="Uptime",
        entity_registry_enabled_default=False,
        device_class=SensorDeviceClass.TIMESTAMP,
        entity_category=EntityCategory.DIAGNOSTIC,
        value=lambda data: data.uptime,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Add Brother entities from a config_entry."""
    coordinator = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id]

    # Due to the change of the attribute name of one sensor, it is necessary to migrate
    # the unique_id to the new one.
    entity_registry = er.async_get(hass)
    old_unique_id = f"{coordinator.data.serial.lower()}_b/w_counter"
    if entity_id := entity_registry.async_get_entity_id(
        PLATFORM, DOMAIN, old_unique_id
    ):
        new_unique_id = f"{coordinator.data.serial.lower()}_bw_counter"
        _LOGGER.debug(
            "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
            entity_id,
            old_unique_id,
            new_unique_id,
        )
        entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id)

    sensors = []

    device_info = DeviceInfo(
        configuration_url=f"http://{entry.data[CONF_HOST]}/",
        identifiers={(DOMAIN, coordinator.data.serial)},
        manufacturer="Brother",
        model=coordinator.data.model,
        name=coordinator.data.model,
        sw_version=coordinator.data.firmware,
    )

    for description in SENSOR_TYPES:
        if description.value(coordinator.data) is not None:
            sensors.append(BrotherPrinterSensor(coordinator, description, device_info))
    async_add_entities(sensors, False)


class BrotherPrinterSensor(
    CoordinatorEntity[BrotherDataUpdateCoordinator], SensorEntity
):
    """Define an Brother Printer sensor."""

    _attr_has_entity_name = True
    entity_description: BrotherSensorEntityDescription

    def __init__(
        self,
        coordinator: BrotherDataUpdateCoordinator,
        description: BrotherSensorEntityDescription,
        device_info: DeviceInfo,
    ) -> None:
        """Initialize."""
        super().__init__(coordinator)
        self._attr_device_info = device_info
        self._attr_native_value = description.value(coordinator.data)
        self._attr_unique_id = f"{coordinator.data.serial.lower()}_{description.key}"
        self.entity_description = description

    @callback
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        self._attr_native_value = self.entity_description.value(self.coordinator.data)
        self.async_write_ha_state()
