"""Binary sensor platform for Nest Protect."""

from __future__ import annotations

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

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.helpers.entity import EntityCategory

from . import HomeAssistantNestProtectData
from .const import DOMAIN
from .entity import NestDescriptiveEntity


@dataclass
class NestProtectBinarySensorDescriptionMixin:
    """Define an entity description mixin for binary sensor entities."""

    value_fn: Callable[[Any], bool]


@dataclass
class NestProtectBinarySensorDescription(
    BinarySensorEntityDescription, NestProtectBinarySensorDescriptionMixin
):
    """Class to describe a Nest Protect binary sensor."""

    wired_only: bool = False


BINARY_SENSOR_DESCRIPTIONS: list[BinarySensorEntityDescription] = [
    NestProtectBinarySensorDescription(
        key="co_status",
        name="CO Status",
        device_class=BinarySensorDeviceClass.CO,
        value_fn=lambda state: state != 0,
    ),
    NestProtectBinarySensorDescription(
        key="smoke_status",
        name="Smoke Status",
        device_class=BinarySensorDeviceClass.SMOKE,
        value_fn=lambda state: state != 0,
    ),
    NestProtectBinarySensorDescription(
        key="heat_status",
        name="Heat Status",
        device_class=BinarySensorDeviceClass.HEAT,
        value_fn=lambda state: state != 0,
    ),
    NestProtectBinarySensorDescription(
        key="component_speaker_test_passed",
        name="Speaker Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:speaker-wireless",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="battery_health_state",
        name="Battery Health",
        device_class=BinarySensorDeviceClass.BATTERY,
        entity_category=EntityCategory.DIAGNOSTIC,
        value_fn=lambda state: state,
    ),
    NestProtectBinarySensorDescription(
        key="is_online",
        name="Online",
        device_class=BinarySensorDeviceClass.CONNECTIVITY,
        entity_category=EntityCategory.DIAGNOSTIC,
        value_fn=lambda state: state,
    ),
    NestProtectBinarySensorDescription(
        key="component_smoke_test_passed",
        name="Smoke Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:smoke",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="component_co_test_passed",
        name="CO Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:molecule-co",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="component_wifi_test_passed",
        name="WiFi Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:wifi",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="component_led_test_passed",
        name="LED Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:led-off",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="component_pir_test_passed",
        name="PIR Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:run",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="component_buzzer_test_passed",
        name="Buzzer Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:alarm-bell",
        value_fn=lambda state: not state,
    ),
    # Disabled for now, since it seems like this state is not valid
    # NestProtectBinarySensorDescription(
    #     key="component_heat_test_passed",
    #     name="Heat Test",
    #     device_class=BinarySensorDeviceClass.PROBLEM,
    #     entity_category=EntityCategory.DIAGNOSTIC,
    #     icon="mdi:fire",
    #     value_fn=lambda state: not state
    # ),
    NestProtectBinarySensorDescription(
        key="component_hum_test_passed",
        name="Humidity Test",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:water-percent",
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="removed_from_base",
        name="Removed from Base",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
        icon="mdi:tray-remove",
        value_fn=lambda state: state,
    ),
    NestProtectBinarySensorDescription(
        key="auto_away",
        name="Occupancy",
        device_class=BinarySensorDeviceClass.OCCUPANCY,
        wired_only=True,
        value_fn=lambda state: not state,
    ),
    NestProtectBinarySensorDescription(
        key="line_power_present",
        name="Line Power",
        device_class=BinarySensorDeviceClass.POWER,
        entity_category=EntityCategory.DIAGNOSTIC,
        wired_only=True,
        value_fn=lambda state: state,
    ),
]


async def async_setup_entry(hass, entry, async_add_devices):
    """Set up the Nest Protect binary sensors from a config entry."""

    data: HomeAssistantNestProtectData = hass.data[DOMAIN][entry.entry_id]
    entities: list[NestProtectBinarySensor] = []

    supported_keys: dict[str, NestProtectBinarySensorDescription] = {
        description.key: description for description in BINARY_SENSOR_DESCRIPTIONS
    }

    for device in data.devices.values():
        for key in device.value:
            if description := supported_keys.get(key):
                # Not all entities are useful for battery powered Nest Protect devices
                if description.wired_only and device.value["wired_or_battery"] != 0:
                    continue

                entities.append(
                    NestProtectBinarySensor(
                        device, description, data.areas, data.client
                    )
                )

    async_add_devices(entities)


class NestProtectBinarySensor(NestDescriptiveEntity, BinarySensorEntity):
    """Representation of a Nest Protect Binary Sensor."""

    entity_description: NestProtectBinarySensorDescription

    @property
    def is_on(self) -> bool:
        """Return the state of the sensor."""
        state = self.bucket.value.get(self.entity_description.key)
        return self.entity_description.value_fn(state)
