"""Support for EDP re:dy."""
from datetime import timedelta
import logging

import voluptuous as vol

from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_START
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client, discovery, dispatcher
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_point_in_time
from homeassistant.util import dt as dt_util

_LOGGER = logging.getLogger(__name__)

DOMAIN = "edp_redy"
EDP_REDY = "edp_redy"
DATA_UPDATE_TOPIC = "{0}_data_update".format(DOMAIN)
UPDATE_INTERVAL = 60

CONFIG_SCHEMA = vol.Schema(
    {
        DOMAIN: vol.Schema(
            {
                vol.Required(CONF_USERNAME): cv.string,
                vol.Required(CONF_PASSWORD): cv.string,
            }
        )
    },
    extra=vol.ALLOW_EXTRA,
)


async def async_setup(hass, config):
    """Set up the EDP re:dy component."""
    from edp_redy import EdpRedySession

    session = EdpRedySession(
        config[DOMAIN][CONF_USERNAME],
        config[DOMAIN][CONF_PASSWORD],
        aiohttp_client.async_get_clientsession(hass),
        hass.loop,
    )
    hass.data[EDP_REDY] = session
    platform_loaded = False

    async def async_update_and_sched(time):
        update_success = await session.async_update()

        if update_success:
            nonlocal platform_loaded
            # pylint: disable=used-before-assignment
            if not platform_loaded:
                for component in ["sensor", "switch"]:
                    await discovery.async_load_platform(
                        hass, component, DOMAIN, {}, config
                    )
                platform_loaded = True

            dispatcher.async_dispatcher_send(hass, DATA_UPDATE_TOPIC)

        # schedule next update
        async_track_point_in_time(
            hass, async_update_and_sched, time + timedelta(seconds=UPDATE_INTERVAL)
        )

    async def start_component(event):
        _LOGGER.debug("Starting updates")
        await async_update_and_sched(dt_util.utcnow())

    # only start fetching data after HA boots to prevent delaying the boot
    # process
    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_component)

    return True


class EdpRedyDevice(Entity):
    """Representation a base re:dy device."""

    def __init__(self, session, device_id, name):
        """Initialize the device."""
        self._session = session
        self._state = None
        self._is_available = True
        self._device_state_attributes = {}
        self._id = device_id
        self._unique_id = device_id
        self._name = name if name else device_id

    async def async_added_to_hass(self):
        """Subscribe to the data updates topic."""
        dispatcher.async_dispatcher_connect(
            self.hass, DATA_UPDATE_TOPIC, self._data_updated
        )

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

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

    @property
    def available(self):
        """Return True if entity is available."""
        return self._is_available

    @property
    def should_poll(self):
        """Return the polling state. No polling needed."""
        return False

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

    @callback
    def _data_updated(self):
        """Update state, trigger updates."""
        self.async_schedule_update_ha_state(True)

    def _parse_data(self, data):
        """Parse data received from the server."""
        if "OutOfOrder" in data:
            try:
                self._is_available = not data["OutOfOrder"]
            except ValueError:
                _LOGGER.error("Could not parse OutOfOrder for %s", self._id)
                self._is_available = False
