"""Support for user- and CDC-based flu info sensors from Flu Near You."""
from datetime import timedelta
import logging

from pyflunearyou import Client
from pyflunearyou.errors import FluNearYouError
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
    ATTR_ATTRIBUTION,
    ATTR_STATE,
    CONF_LATITUDE,
    CONF_LONGITUDE,
    CONF_MONITORED_CONDITIONS,
)
from homeassistant.helpers import aiohttp_client
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle

_LOGGER = logging.getLogger(__name__)

ATTR_CITY = "city"
ATTR_REPORTED_DATE = "reported_date"
ATTR_REPORTED_LATITUDE = "reported_latitude"
ATTR_REPORTED_LONGITUDE = "reported_longitude"
ATTR_STATE_REPORTS_LAST_WEEK = "state_reports_last_week"
ATTR_STATE_REPORTS_THIS_WEEK = "state_reports_this_week"
ATTR_ZIP_CODE = "zip_code"

DEFAULT_ATTRIBUTION = "Data provided by Flu Near You"

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
SCAN_INTERVAL = timedelta(minutes=30)

CATEGORY_CDC_REPORT = "cdc_report"
CATEGORY_USER_REPORT = "user_report"

TYPE_CDC_LEVEL = "level"
TYPE_CDC_LEVEL2 = "level2"
TYPE_USER_CHICK = "chick"
TYPE_USER_DENGUE = "dengue"
TYPE_USER_FLU = "flu"
TYPE_USER_LEPTO = "lepto"
TYPE_USER_NO_SYMPTOMS = "none"
TYPE_USER_SYMPTOMS = "symptoms"
TYPE_USER_TOTAL = "total"

EXTENDED_TYPE_MAPPING = {
    TYPE_USER_FLU: "ili",
    TYPE_USER_NO_SYMPTOMS: "no_symptoms",
    TYPE_USER_TOTAL: "total_surveys",
}

SENSORS = {
    CATEGORY_CDC_REPORT: [
        (TYPE_CDC_LEVEL, "CDC Level", "mdi:biohazard", None),
        (TYPE_CDC_LEVEL2, "CDC Level 2", "mdi:biohazard", None),
    ],
    CATEGORY_USER_REPORT: [
        (TYPE_USER_CHICK, "Avian Flu Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_DENGUE, "Dengue Fever Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_FLU, "Flu Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_LEPTO, "Leptospirosis Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_NO_SYMPTOMS, "No Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_SYMPTOMS, "Flu-like Symptoms", "mdi:alert", "reports"),
        (TYPE_USER_TOTAL, "Total Symptoms", "mdi:alert", "reports"),
    ],
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Optional(CONF_LATITUDE): cv.latitude,
        vol.Optional(CONF_LONGITUDE): cv.longitude,
        vol.Required(CONF_MONITORED_CONDITIONS, default=list(SENSORS)): vol.All(
            cv.ensure_list, [vol.In(SENSORS)]
        ),
    }
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Configure the platform and add the sensors."""
    websession = aiohttp_client.async_get_clientsession(hass)

    latitude = config.get(CONF_LATITUDE, hass.config.latitude)
    longitude = config.get(CONF_LONGITUDE, hass.config.longitude)

    fny = FluNearYouData(
        Client(websession), latitude, longitude, config[CONF_MONITORED_CONDITIONS]
    )
    await fny.async_update()

    sensors = [
        FluNearYouSensor(fny, kind, name, category, icon, unit)
        for category in config[CONF_MONITORED_CONDITIONS]
        for kind, name, icon, unit in SENSORS[category]
    ]

    async_add_entities(sensors, True)


class FluNearYouSensor(Entity):
    """Define a base Flu Near You sensor."""

    def __init__(self, fny, kind, name, category, icon, unit):
        """Initialize the sensor."""
        self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
        self._category = category
        self._icon = icon
        self._kind = kind
        self._name = name
        self._state = None
        self._unit = unit
        self.fny = fny

    @property
    def available(self):
        """Return True if entity is available."""
        return bool(self.fny.data[self._category])

    @property
    def device_state_attributes(self):
        """Return the device state attributes."""
        return self._attrs

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

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

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

    @property
    def unique_id(self):
        """Return a unique, HASS-friendly identifier for this entity."""
        return f"{self.fny.latitude},{self.fny.longitude}_{self._kind}"

    @property
    def unit_of_measurement(self):
        """Return the unit the value is expressed in."""
        return self._unit

    async def async_update(self):
        """Update the sensor."""
        await self.fny.async_update()

        cdc_data = self.fny.data.get(CATEGORY_CDC_REPORT)
        user_data = self.fny.data.get(CATEGORY_USER_REPORT)

        if self._category == CATEGORY_CDC_REPORT and cdc_data:
            self._attrs.update(
                {
                    ATTR_REPORTED_DATE: cdc_data["week_date"],
                    ATTR_STATE: cdc_data["name"],
                }
            )
            self._state = cdc_data[self._kind]
        elif self._category == CATEGORY_USER_REPORT and user_data:
            self._attrs.update(
                {
                    ATTR_CITY: user_data["local"]["city"].split("(")[0],
                    ATTR_REPORTED_LATITUDE: user_data["local"]["latitude"],
                    ATTR_REPORTED_LONGITUDE: user_data["local"]["longitude"],
                    ATTR_STATE: user_data["state"]["name"],
                    ATTR_ZIP_CODE: user_data["local"]["zip"],
                }
            )

            if self._kind in user_data["state"]["data"]:
                states_key = self._kind
            elif self._kind in EXTENDED_TYPE_MAPPING:
                states_key = EXTENDED_TYPE_MAPPING[self._kind]

            self._attrs[ATTR_STATE_REPORTS_THIS_WEEK] = user_data["state"]["data"][
                states_key
            ]
            self._attrs[ATTR_STATE_REPORTS_LAST_WEEK] = user_data["state"][
                "last_week_data"
            ][states_key]

            if self._kind == TYPE_USER_TOTAL:
                self._state = sum(
                    v
                    for k, v in user_data["local"].items()
                    if k
                    in (
                        TYPE_USER_CHICK,
                        TYPE_USER_DENGUE,
                        TYPE_USER_FLU,
                        TYPE_USER_LEPTO,
                        TYPE_USER_SYMPTOMS,
                    )
                )
            else:
                self._state = user_data["local"][self._kind]


class FluNearYouData:
    """Define a data object to retrieve info from Flu Near You."""

    def __init__(self, client, latitude, longitude, sensor_types):
        """Initialize."""
        self._client = client
        self._sensor_types = sensor_types
        self.data = {}
        self.latitude = latitude
        self.longitude = longitude

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    async def async_update(self):
        """Update Flu Near You data."""
        for key, method in [
            (CATEGORY_CDC_REPORT, self._client.cdc_reports.status_by_coordinates),
            (CATEGORY_USER_REPORT, self._client.user_reports.status_by_coordinates),
        ]:
            if key in self._sensor_types:
                try:
                    self.data[key] = await method(self.latitude, self.longitude)
                except FluNearYouError as err:
                    _LOGGER.error('There was an error with "%s" data: %s', key, err)
                    self.data[key] = {}

        _LOGGER.debug("New data stored: %s", self.data)