"""Support for Tibber binary sensors."""

from __future__ import annotations

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

import tibber
from tibber.data_api import TibberDevice

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, TibberConfigEntry
from .coordinator import TibberDataAPICoordinator

_LOGGER = logging.getLogger(__name__)


@dataclass(frozen=True, kw_only=True)
class TibberBinarySensorEntityDescription(BinarySensorEntityDescription):
    """Describes Tibber binary sensor entity."""

    is_on_fn: Callable[[str], bool | None]


DATA_API_BINARY_SENSORS: tuple[TibberBinarySensorEntityDescription, ...] = (
    TibberBinarySensorEntityDescription(
        key="connector.status",
        device_class=BinarySensorDeviceClass.PLUG,
        is_on_fn={"connected": True, "disconnected": False}.get,
    ),
    TibberBinarySensorEntityDescription(
        key="charging.status",
        device_class=BinarySensorDeviceClass.BATTERY_CHARGING,
        is_on_fn={"charging": True, "idle": False}.get,
    ),
    TibberBinarySensorEntityDescription(
        key="onOff",
        device_class=BinarySensorDeviceClass.POWER,
        is_on_fn={"on": True, "off": False}.get,
    ),
    TibberBinarySensorEntityDescription(
        key="isOnline",
        device_class=BinarySensorDeviceClass.CONNECTIVITY,
        is_on_fn=lambda v: v.lower() == "true",
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: TibberConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the Tibber binary sensors."""
    coordinator = entry.runtime_data.data_api_coordinator
    assert coordinator is not None

    entities: list[TibberDataAPIBinarySensor] = []
    api_binary_sensors = {sensor.key: sensor for sensor in DATA_API_BINARY_SENSORS}

    for device in coordinator.data.values():
        for sensor in device.sensors:
            description: TibberBinarySensorEntityDescription | None = (
                api_binary_sensors.get(sensor.id)
            )
            if description is None:
                continue
            entities.append(TibberDataAPIBinarySensor(coordinator, device, description))
    async_add_entities(entities)


class TibberDataAPIBinarySensor(
    CoordinatorEntity[TibberDataAPICoordinator], BinarySensorEntity
):
    """Representation of a Tibber Data API binary sensor."""

    _attr_has_entity_name = True
    entity_description: TibberBinarySensorEntityDescription

    def __init__(
        self,
        coordinator: TibberDataAPICoordinator,
        device: TibberDevice,
        entity_description: TibberBinarySensorEntityDescription,
    ) -> None:
        """Initialize the binary sensor."""
        super().__init__(coordinator)

        self._device_id: str = device.id
        self.entity_description = entity_description

        self._attr_unique_id = f"{device.id}_{entity_description.key}"

        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, device.external_id)},
            name=device.name,
            manufacturer=device.brand,
            model=device.model,
        )

    @property
    def available(self) -> bool:
        """Return if entity is available."""
        return (
            super().available and self._device_id in self.coordinator.sensors_by_device
        )

    @property
    def device(self) -> dict[str, tibber.data_api.Sensor]:
        """Return the device sensors."""
        return self.coordinator.sensors_by_device[self._device_id]

    @property
    def is_on(self) -> bool | None:
        """Return the state of the binary sensor."""
        return self.entity_description.is_on_fn(
            str(self.device[self.entity_description.key].value)
        )
