"""Support for SwitchBot Cloud binary sensors."""

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

from switchbot_api import Device, SwitchBotAPI

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import SwitchbotCloudData
from .const import DOMAIN
from .coordinator import SwitchBotCoordinator
from .entity import SwitchBotCloudEntity


@dataclass(frozen=True)
class SwitchBotCloudBinarySensorEntityDescription(BinarySensorEntityDescription):
    """Describes a Switchbot Cloud binary sensor."""

    # Value or values to consider binary sensor to be "on"
    on_value: bool | str = True
    value_fn: Callable[[dict[str, Any]], bool | None] | None = None


CALIBRATION_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="calibrate",
    name="Calibration",
    translation_key="calibration",
    device_class=BinarySensorDeviceClass.PROBLEM,
    entity_category=EntityCategory.DIAGNOSTIC,
    on_value=False,
)

DOOR_OPEN_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="doorState",
    device_class=BinarySensorDeviceClass.DOOR,
    on_value="opened",
)

MOVE_DETECTED_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="moveDetected",
    device_class=BinarySensorDeviceClass.MOTION,
    value_fn=(
        lambda data: (
            data.get("moveDetected") is True
            or data.get("detectionState") == "DETECTED"
            or data.get("detected") is True
        )
    ),
)

IS_LIGHT_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="brightness",
    device_class=BinarySensorDeviceClass.LIGHT,
    on_value="bright",
)

LEAK_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="status",
    device_class=BinarySensorDeviceClass.MOISTURE,
    value_fn=lambda data: any(data.get(key) for key in ("status", "detectionState")),
)

OPEN_DESCRIPTION = SwitchBotCloudBinarySensorEntityDescription(
    key="openState",
    device_class=BinarySensorDeviceClass.OPENING,
    value_fn=lambda data: data.get("openState") in ("open", "timeOutNotClose"),
)


BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES = {
    "Smart Lock": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Lite": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Pro": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Ultra": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Vision": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Vision Pro": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Smart Lock Pro Wifi": (
        CALIBRATION_DESCRIPTION,
        DOOR_OPEN_DESCRIPTION,
    ),
    "Curtain": (CALIBRATION_DESCRIPTION,),
    "Curtain3": (CALIBRATION_DESCRIPTION,),
    "Roller Shade": (CALIBRATION_DESCRIPTION,),
    "Blind Tilt": (CALIBRATION_DESCRIPTION,),
    "Garage Door Opener": (DOOR_OPEN_DESCRIPTION,),
    "Motion Sensor": (MOVE_DETECTED_DESCRIPTION,),
    "Contact Sensor": (
        MOVE_DETECTED_DESCRIPTION,
        IS_LIGHT_DESCRIPTION,
        OPEN_DESCRIPTION,
    ),
    "Hub 3": (MOVE_DETECTED_DESCRIPTION,),
    "Water Detector": (LEAK_DESCRIPTION,),
    "Climate Panel": (
        IS_LIGHT_DESCRIPTION,
        MOVE_DETECTED_DESCRIPTION,
    ),
    "Presence Sensor": (MOVE_DETECTED_DESCRIPTION,),
}


async def async_setup_entry(
    hass: HomeAssistant,
    config: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up SwitchBot Cloud entry."""
    data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]

    async_add_entities(
        SwitchBotCloudBinarySensor(data.api, device, coordinator, description)
        for device, coordinator in data.devices.binary_sensors
        for description in BINARY_SENSOR_DESCRIPTIONS_BY_DEVICE_TYPES[
            device.device_type
        ]
    )


class SwitchBotCloudBinarySensor(SwitchBotCloudEntity, BinarySensorEntity):
    """Representation of a Switchbot binary sensor."""

    entity_description: SwitchBotCloudBinarySensorEntityDescription

    def __init__(
        self,
        api: SwitchBotAPI,
        device: Device,
        coordinator: SwitchBotCoordinator,
        description: SwitchBotCloudBinarySensorEntityDescription,
    ) -> None:
        """Initialize SwitchBot Cloud sensor entity."""
        super().__init__(api, device, coordinator)
        self.entity_description = description
        self._attr_unique_id = f"{device.device_id}_{description.key}"

    def _set_attributes(self) -> None:
        """Set attributes from coordinator data."""
        if not self.coordinator.data:
            return

        if self.entity_description.value_fn:
            self._attr_is_on = self.entity_description.value_fn(self.coordinator.data)
            return

        self._attr_is_on = (
            self.coordinator.data.get(self.entity_description.key)
            == self.entity_description.on_value
        )
