"""This component provides HA sensor support for Ring Door Bell/Chimes."""
import logging

from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.icon import icon_for_battery_level

from . import (
    ATTRIBUTION,
    DATA_HEALTH_DATA_TRACKER,
    DATA_HISTORY,
    DOMAIN,
    SIGNAL_UPDATE_HEALTH_RING,
    SIGNAL_UPDATE_RING,
)

_LOGGER = logging.getLogger(__name__)

# Sensor types: Name, category, units, icon, kind, device_class
SENSOR_TYPES = {
    "battery": [
        "Battery",
        ["doorbots", "authorized_doorbots", "stickup_cams"],
        "%",
        None,
        None,
        "battery",
    ],
    "last_activity": [
        "Last Activity",
        ["doorbots", "authorized_doorbots", "stickup_cams"],
        None,
        "history",
        None,
        "timestamp",
    ],
    "last_ding": [
        "Last Ding",
        ["doorbots", "authorized_doorbots"],
        None,
        "history",
        "ding",
        "timestamp",
    ],
    "last_motion": [
        "Last Motion",
        ["doorbots", "authorized_doorbots", "stickup_cams"],
        None,
        "history",
        "motion",
        "timestamp",
    ],
    "volume": [
        "Volume",
        ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"],
        None,
        "bell-ring",
        None,
        None,
    ],
    "wifi_signal_category": [
        "WiFi Signal Category",
        ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"],
        None,
        "wifi",
        None,
        None,
    ],
    "wifi_signal_strength": [
        "WiFi Signal Strength",
        ["chimes", "doorbots", "authorized_doorbots", "stickup_cams"],
        "dBm",
        "wifi",
        None,
        "signal_strength",
    ],
}


async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up a sensor for a Ring device."""
    ring = hass.data[DOMAIN][config_entry.entry_id]
    devices = ring.devices()
    # Makes a ton of requests. We will make this a config entry option in the future
    wifi_enabled = False

    sensors = []

    for device_type in ("chimes", "doorbots", "authorized_doorbots", "stickup_cams"):
        for sensor_type in SENSOR_TYPES:
            if device_type not in SENSOR_TYPES[sensor_type][1]:
                continue

            if not wifi_enabled and sensor_type.startswith("wifi_"):
                continue

            for device in devices[device_type]:
                if device_type == "battery" and device.battery_life is None:
                    continue

                sensors.append(RingSensor(config_entry.entry_id, device, sensor_type))

    async_add_entities(sensors, True)


class RingSensor(Entity):
    """A sensor implementation for Ring device."""

    def __init__(self, config_entry_id, device, sensor_type):
        """Initialize a sensor for Ring device."""
        self._config_entry_id = config_entry_id
        self._sensor_type = sensor_type
        self._device = device
        self._extra = None
        self._icon = "mdi:{}".format(SENSOR_TYPES.get(self._sensor_type)[3])
        self._kind = SENSOR_TYPES.get(self._sensor_type)[4]
        self._name = "{0} {1}".format(
            self._device.name, SENSOR_TYPES.get(self._sensor_type)[0]
        )
        self._state = None
        self._unique_id = f"{self._device.id}-{self._sensor_type}"
        self._disp_disconnect = None
        self._disp_disconnect_health = None

    async def async_added_to_hass(self):
        """Register callbacks."""
        self._disp_disconnect = async_dispatcher_connect(
            self.hass, SIGNAL_UPDATE_RING, self._update_callback
        )
        if self._sensor_type not in ("wifi_signal_category", "wifi_signal_strength"):
            return

        self._disp_disconnect_health = async_dispatcher_connect(
            self.hass, SIGNAL_UPDATE_HEALTH_RING, self._update_callback
        )
        await self.hass.data[DATA_HEALTH_DATA_TRACKER].track_device(
            self._config_entry_id, self._device
        )
        # Write the state, it was not available when doing initial update.
        if self._sensor_type == "wifi_signal_category":
            self._state = self._device.wifi_signal_category

        if self._sensor_type == "wifi_signal_strength":
            self._state = self._device.wifi_signal_strength

    async def async_will_remove_from_hass(self):
        """Disconnect callbacks."""
        if self._disp_disconnect:
            self._disp_disconnect()
            self._disp_disconnect = None

        if self._disp_disconnect_health:
            self._disp_disconnect_health()
            self._disp_disconnect_health = None

        if self._sensor_type not in ("wifi_signal_category", "wifi_signal_strength"):
            return

        self.hass.data[DATA_HEALTH_DATA_TRACKER].untrack_device(
            self._config_entry_id, self._device
        )

    @callback
    def _update_callback(self):
        """Call update method."""
        self.async_schedule_update_ha_state(True)

    @property
    def should_poll(self):
        """Return False, updates are controlled via the hub."""
        return False

    @property
    def name(self):
        """Return the name of the sensor."""
        return self._name

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def unique_id(self):
        """Return a unique ID."""
        return self._unique_id

    @property
    def device_class(self):
        """Return sensor device class."""
        return SENSOR_TYPES[self._sensor_type][5]

    @property
    def device_info(self):
        """Return device info."""
        return {
            "identifiers": {(DOMAIN, self._device.device_id)},
            "name": self._device.name,
            "model": self._device.model,
            "manufacturer": "Ring",
        }

    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        attrs = {}

        attrs[ATTR_ATTRIBUTION] = ATTRIBUTION

        if self._extra and self._sensor_type.startswith("last_"):
            attrs["created_at"] = self._extra["created_at"]
            attrs["answered"] = self._extra["answered"]
            attrs["recording_status"] = self._extra["recording"]["status"]
            attrs["category"] = self._extra["kind"]

        return attrs

    @property
    def icon(self):
        """Icon to use in the frontend, if any."""
        if self._sensor_type == "battery" and self._state is not None:
            return icon_for_battery_level(
                battery_level=int(self._state), charging=False
            )
        return self._icon

    @property
    def unit_of_measurement(self):
        """Return the units of measurement."""
        return SENSOR_TYPES.get(self._sensor_type)[2]

    async def async_update(self):
        """Get the latest data and updates the state."""
        _LOGGER.debug("Updating data from %s sensor", self._name)

        if self._sensor_type == "volume":
            self._state = self._device.volume

        if self._sensor_type == "battery":
            self._state = self._device.battery_life

        if self._sensor_type.startswith("last_"):
            history = await self.hass.data[DATA_HISTORY].async_get_history(
                self._config_entry_id, self._device
            )

            found = None
            for entry in history:
                if entry["kind"] == self._kind:
                    found = entry
                    break

            if found:
                self._extra = found
                created_at = found["created_at"]
                self._state = created_at.isoformat()

        if self._sensor_type == "wifi_signal_category":
            self._state = self._device.wifi_signal_category

        if self._sensor_type == "wifi_signal_strength":
            self._state = self._device.wifi_signal_strength