"""Support for Efergy sensors."""
from __future__ import annotations

import logging

from pyefergy import Efergy, exceptions
import voluptuous as vol

from homeassistant.components.sensor import (
    PLATFORM_SCHEMA,
    SensorEntity,
    SensorEntityDescription,
)
from homeassistant.const import (
    CONF_CURRENCY,
    CONF_MONITORED_VARIABLES,
    CONF_TYPE,
    DEVICE_CLASS_ENERGY,
    DEVICE_CLASS_MONETARY,
    DEVICE_CLASS_POWER,
    ENERGY_KILO_WATT_HOUR,
    POWER_WATT,
)
from homeassistant.core import HomeAssistant
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_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

CONF_APPTOKEN = "app_token"
CONF_UTC_OFFSET = "utc_offset"

CONF_PERIOD = "period"

CONF_INSTANT = "instant_readings"
CONF_AMOUNT = "amount"
CONF_BUDGET = "budget"
CONF_COST = "cost"
CONF_CURRENT_VALUES = "current_values"

DEFAULT_PERIOD = "year"
DEFAULT_UTC_OFFSET = "0"

_LOGGER = logging.getLogger(__name__)

SENSOR_TYPES: dict[str, SensorEntityDescription] = {
    CONF_INSTANT: SensorEntityDescription(
        key=CONF_INSTANT,
        name="Energy Usage",
        device_class=DEVICE_CLASS_POWER,
        native_unit_of_measurement=POWER_WATT,
    ),
    CONF_AMOUNT: SensorEntityDescription(
        key=CONF_AMOUNT,
        name="Energy Consumed",
        device_class=DEVICE_CLASS_ENERGY,
        native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
    ),
    CONF_BUDGET: SensorEntityDescription(
        key=CONF_BUDGET,
        name="Energy Budget",
    ),
    CONF_COST: SensorEntityDescription(
        key=CONF_COST,
        name="Energy Cost",
        device_class=DEVICE_CLASS_MONETARY,
    ),
    CONF_CURRENT_VALUES: SensorEntityDescription(
        key=CONF_CURRENT_VALUES,
        name="Per-Device Usage",
        device_class=DEVICE_CLASS_POWER,
        native_unit_of_measurement=POWER_WATT,
    ),
}

TYPES_SCHEMA = vol.In(SENSOR_TYPES)

SENSORS_SCHEMA = vol.Schema(
    {
        vol.Required(CONF_TYPE): TYPES_SCHEMA,
        vol.Optional(CONF_CURRENCY, default=""): cv.string,
        vol.Optional(CONF_PERIOD, default=DEFAULT_PERIOD): cv.string,
    }
)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_APPTOKEN): cv.string,
        vol.Optional(CONF_UTC_OFFSET, default=DEFAULT_UTC_OFFSET): cv.string,
        vol.Required(CONF_MONITORED_VARIABLES): [SENSORS_SCHEMA],
    }
)


async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType = None,
) -> None:
    """Set up the Efergy sensor."""
    api = Efergy(
        config[CONF_APPTOKEN],
        async_get_clientsession(hass),
        utc_offset=config[CONF_UTC_OFFSET],
    )

    dev = []
    try:
        sensors = await api.get_sids()
    except (exceptions.DataError, exceptions.ConnectTimeout) as ex:
        raise PlatformNotReady("Error getting data from Efergy:") from ex
    for variable in config[CONF_MONITORED_VARIABLES]:
        if variable[CONF_TYPE] == CONF_CURRENT_VALUES:
            for sensor in sensors:
                dev.append(
                    EfergySensor(
                        api,
                        variable[CONF_PERIOD],
                        variable[CONF_CURRENCY],
                        SENSOR_TYPES[variable[CONF_TYPE]],
                        sid=sensor["sid"],
                    )
                )
        dev.append(
            EfergySensor(
                api,
                variable[CONF_PERIOD],
                variable[CONF_CURRENCY],
                SENSOR_TYPES[variable[CONF_TYPE]],
            )
        )

    add_entities(dev, True)


class EfergySensor(SensorEntity):
    """Implementation of an Efergy sensor."""

    def __init__(
        self,
        api: Efergy,
        period: str,
        currency: str,
        description: SensorEntityDescription,
        sid: str = None,
    ) -> None:
        """Initialize the sensor."""
        self.entity_description = description
        self.sid = sid
        self.api = api
        self.period = period
        if sid:
            self._attr_name = f"efergy_{sid}"
        if description.key == CONF_COST:
            self._attr_native_unit_of_measurement = f"{currency}/{period}"

    async def async_update(self) -> None:
        """Get the Efergy monitor data from the web service."""
        try:
            self._attr_native_value = await self.api.async_get_reading(
                self.entity_description.key, period=self.period, sid=self.sid
            )
        except (exceptions.DataError, exceptions.ConnectTimeout) as ex:
            if self._attr_available:
                self._attr_available = False
                _LOGGER.error("Error getting data from Efergy: %s", ex)
            return
        if not self._attr_available:
            self._attr_available = True
            _LOGGER.info("Connection to Efergy has resumed")
