"""Support for consuming values for the Volkszaehler API."""
from datetime import timedelta
import logging

from volkszaehler import Volkszaehler
from volkszaehler.exceptions import VolkszaehlerApiConnectionError
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
    CONF_HOST,
    CONF_MONITORED_CONDITIONS,
    CONF_NAME,
    CONF_PORT,
    ENERGY_WATT_HOUR,
    POWER_WATT,
)
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle

_LOGGER = logging.getLogger(__name__)

CONF_UUID = "uuid"

DEFAULT_HOST = "localhost"
DEFAULT_NAME = "Volkszaehler"
DEFAULT_PORT = 80

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)

SENSOR_TYPES = {
    "average": ["Average", POWER_WATT, "mdi:power-off"],
    "consumption": ["Consumption", ENERGY_WATT_HOUR, "mdi:power-plug"],
    "max": ["Max", POWER_WATT, "mdi:arrow-up"],
    "min": ["Min", POWER_WATT, "mdi:arrow-down"],
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_UUID): cv.string,
        vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
        vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
        vol.Optional(CONF_MONITORED_CONDITIONS, default=["average"]): vol.All(
            cv.ensure_list, [vol.In(SENSOR_TYPES)]
        ),
    }
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the Volkszaehler sensors."""

    host = config[CONF_HOST]
    name = config[CONF_NAME]
    port = config[CONF_PORT]
    uuid = config[CONF_UUID]
    conditions = config[CONF_MONITORED_CONDITIONS]

    session = async_get_clientsession(hass)
    vz_api = VolkszaehlerData(
        Volkszaehler(hass.loop, session, uuid, host=host, port=port)
    )

    await vz_api.async_update()

    if vz_api.api.data is None:
        raise PlatformNotReady

    dev = []
    for condition in conditions:
        dev.append(VolkszaehlerSensor(vz_api, name, condition))

    async_add_entities(dev, True)


class VolkszaehlerSensor(Entity):
    """Implementation of a Volkszaehler sensor."""

    def __init__(self, vz_api, name, sensor_type):
        """Initialize the Volkszaehler sensor."""
        self.vz_api = vz_api
        self._name = name
        self.type = sensor_type
        self._state = None

    @property
    def name(self):
        """Return the name of the sensor."""
        return "{} {}".format(self._name, SENSOR_TYPES[self.type][0])

    @property
    def icon(self):
        """Icon to use in the frontend, if any."""
        return SENSOR_TYPES[self.type][2]

    @property
    def unit_of_measurement(self):
        """Return the unit the value is expressed in."""
        return SENSOR_TYPES[self.type][1]

    @property
    def available(self):
        """Could the device be accessed during the last update call."""
        return self.vz_api.available

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

    async def async_update(self):
        """Get the latest data from REST API."""
        await self.vz_api.async_update()

        if self.vz_api.api.data is not None:
            self._state = round(getattr(self.vz_api.api, self.type), 2)


class VolkszaehlerData:
    """The class for handling the data retrieval from the Volkszaehler API."""

    def __init__(self, api):
        """Initialize the data object."""
        self.api = api
        self.available = True

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    async def async_update(self):
        """Get the latest data from the Volkszaehler REST API."""

        try:
            await self.api.get_data()
            self.available = True
        except VolkszaehlerApiConnectionError:
            _LOGGER.error("Unable to fetch data from the Volkszaehler API")
            self.available = False
