"""Support for Synology DSM Sensors."""
from typing import Dict

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    ATTR_ATTRIBUTION,
    CONF_DISKS,
    DATA_MEGABYTES,
    DATA_RATE_KILOBYTES_PER_SECOND,
    TEMP_CELSIUS,
)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType

from . import SynoApi
from .const import (
    BASE_NAME,
    CONF_VOLUMES,
    DOMAIN,
    STORAGE_DISK_SENSORS,
    STORAGE_VOL_SENSORS,
    TEMP_SENSORS_KEYS,
    UTILISATION_SENSORS,
)

ATTRIBUTION = "Data provided by Synology"


async def async_setup_entry(
    hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
    """Set up the Synology NAS Sensor."""

    api = hass.data[DOMAIN][entry.unique_id]

    sensors = [
        SynoNasUtilSensor(api, sensor_type, UTILISATION_SENSORS[sensor_type])
        for sensor_type in UTILISATION_SENSORS
    ]

    # Handle all volumes
    if api.storage.volumes_ids:
        for volume in entry.data.get(CONF_VOLUMES, api.storage.volumes_ids):
            sensors += [
                SynoNasStorageSensor(
                    api, sensor_type, STORAGE_VOL_SENSORS[sensor_type], volume
                )
                for sensor_type in STORAGE_VOL_SENSORS
            ]

    # Handle all disks
    if api.storage.disks_ids:
        for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids):
            sensors += [
                SynoNasStorageSensor(
                    api, sensor_type, STORAGE_DISK_SENSORS[sensor_type], disk
                )
                for sensor_type in STORAGE_DISK_SENSORS
            ]

    async_add_entities(sensors, True)


class SynoNasSensor(Entity):
    """Representation of a Synology NAS Sensor."""

    def __init__(
        self,
        api: SynoApi,
        sensor_type: str,
        sensor_info: Dict[str, str],
        monitored_device: str = None,
    ):
        """Initialize the sensor."""
        self._api = api
        self.sensor_type = sensor_type
        self._name = f"{BASE_NAME} {sensor_info[0]}"
        self._unit = sensor_info[1]
        self._icon = sensor_info[2]
        self.monitored_device = monitored_device
        self._unique_id = f"{self._api.information.serial}_{sensor_info[0]}"

        if self.monitored_device:
            self._name += f" ({self.monitored_device})"
            self._unique_id += f"_{self.monitored_device}"

        self._unsub_dispatcher = None

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

    @property
    def name(self) -> str:
        """Return the name."""
        return self._name

    @property
    def icon(self) -> str:
        """Return the icon."""
        return self._icon

    @property
    def unit_of_measurement(self) -> str:
        """Return the unit the value is expressed in."""
        if self.sensor_type in TEMP_SENSORS_KEYS:
            return self._api.temp_unit
        return self._unit

    @property
    def device_state_attributes(self) -> Dict[str, any]:
        """Return the state attributes."""
        return {ATTR_ATTRIBUTION: ATTRIBUTION}

    @property
    def device_info(self) -> Dict[str, any]:
        """Return the device information."""
        return {
            "identifiers": {(DOMAIN, self._api.information.serial)},
            "name": "Synology NAS",
            "manufacturer": "Synology",
            "model": self._api.information.model,
            "sw_version": self._api.information.version_string,
        }

    @property
    def should_poll(self) -> bool:
        """No polling needed."""
        return False

    async def async_added_to_hass(self):
        """Register state update callback."""
        self._unsub_dispatcher = async_dispatcher_connect(
            self.hass, self._api.signal_sensor_update, self.async_write_ha_state
        )

    async def async_will_remove_from_hass(self):
        """Clean up after entity before removal."""
        self._unsub_dispatcher()


class SynoNasUtilSensor(SynoNasSensor):
    """Representation a Synology Utilisation Sensor."""

    @property
    def state(self):
        """Return the state."""
        if self._unit == DATA_RATE_KILOBYTES_PER_SECOND or self._unit == DATA_MEGABYTES:
            attr = getattr(self._api.utilisation, self.sensor_type)(False)

            if attr is None:
                return None

            if self._unit == DATA_RATE_KILOBYTES_PER_SECOND:
                return round(attr / 1024.0, 1)
            if self._unit == DATA_MEGABYTES:
                return round(attr / 1024.0 / 1024.0, 1)
        else:
            return getattr(self._api.utilisation, self.sensor_type)


class SynoNasStorageSensor(SynoNasSensor):
    """Representation a Synology Storage Sensor."""

    @property
    def state(self):
        """Return the state."""
        if self.monitored_device:
            if self.sensor_type in TEMP_SENSORS_KEYS:
                attr = getattr(self._api.storage, self.sensor_type)(
                    self.monitored_device
                )

                if attr is None:
                    return None

                if self._api.temp_unit == TEMP_CELSIUS:
                    return attr

                return round(attr * 1.8 + 32.0, 1)

            return getattr(self._api.storage, self.sensor_type)(self.monitored_device)
        return None

    @property
    def device_info(self) -> Dict[str, any]:
        """Return the device information."""
        return {
            "identifiers": {
                (DOMAIN, self._api.information.serial, self.monitored_device)
            },
            "name": f"Synology NAS ({self.monitored_device})",
            "manufacturer": "Synology",
            "model": self._api.information.model,
            "sw_version": self._api.information.version_string,
            "via_device": (DOMAIN, self._api.information.serial),
        }
