"""
Support for Telldus Live.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/tellduslive/
"""
from datetime import datetime, timedelta
import logging

from homeassistant.const import ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util.dt import utcnow
import voluptuous as vol

DOMAIN = 'tellduslive'

REQUIREMENTS = ['tellduslive==0.1.13']

_LOGGER = logging.getLogger(__name__)

CONF_PUBLIC_KEY = 'public_key'
CONF_PRIVATE_KEY = 'private_key'
CONF_TOKEN = 'token'
CONF_TOKEN_SECRET = 'token_secret'
CONF_UPDATE_INTERVAL = 'update_interval'

MIN_UPDATE_INTERVAL = timedelta(seconds=5)
DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1)

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_PUBLIC_KEY): cv.string,
        vol.Required(CONF_PRIVATE_KEY): cv.string,
        vol.Required(CONF_TOKEN): cv.string,
        vol.Required(CONF_TOKEN_SECRET): cv.string,
        vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): (
            vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)))
    }),
}, extra=vol.ALLOW_EXTRA)


ATTR_LAST_UPDATED = 'time_last_updated'


def setup(hass, config):
    """Setup the Telldus Live component."""
    client = TelldusLiveClient(hass, config)

    if not client.validate_session():
        _LOGGER.error(
            'Authentication Error: '
            'Please make sure you have configured your keys '
            'that can be aquired from https://api.telldus.com/keys/index')
        return False

    hass.data[DOMAIN] = client
    client.update(utcnow())

    return True


class TelldusLiveClient(object):
    """Get the latest data and update the states."""

    def __init__(self, hass, config):
        """Initialize the Tellus data object."""
        from tellduslive import Client

        public_key = config[DOMAIN].get(CONF_PUBLIC_KEY)
        private_key = config[DOMAIN].get(CONF_PRIVATE_KEY)
        token = config[DOMAIN].get(CONF_TOKEN)
        token_secret = config[DOMAIN].get(CONF_TOKEN_SECRET)

        self.entities = []

        self._hass = hass
        self._config = config

        self._interval = config[DOMAIN].get(CONF_UPDATE_INTERVAL)
        _LOGGER.debug('Update interval %s', self._interval)

        self._client = Client(public_key,
                              private_key,
                              token,
                              token_secret)

    def validate_session(self):
        """Make a request to see if the session is valid."""
        response = self._client.request_user()
        return response and 'email' in response

    def update(self, now):
        """Periodically poll the servers for current state."""
        _LOGGER.debug('Updating')
        try:
            self._sync()
        finally:
            track_point_in_utc_time(self._hass,
                                    self.update,
                                    now + self._interval)

    def _sync(self):
        """Update local list of devices."""
        self._client.update()

        def identify_device(device):
            """Find out what type of HA component to create."""
            from tellduslive import (DIM, UP, TURNON)
            if device.methods & DIM:
                return 'light'
            elif device.methods & UP:
                return 'cover'
            elif device.methods & TURNON:
                return 'switch'
            else:
                _LOGGER.warning('Unidentified device type (methods: %d)',
                                device.methods)
                return 'switch'

        def discover(device_id, component):
            """Discover the component."""
            discovery.load_platform(self._hass,
                                    component,
                                    DOMAIN,
                                    [device_id],
                                    self._config)

        known_ids = set([entity.device_id for entity in self.entities])
        for device in self._client.devices:
            if device.device_id in known_ids:
                continue
            if device.is_sensor:
                for item_id in device.items:
                    discover((device.device_id,) + item_id,
                             'sensor')
            else:
                discover(device.device_id,
                         identify_device(device))

        for entity in self.entities:
            entity.changed()

    def device(self, device_id):
        """Return device representation."""
        import tellduslive
        return tellduslive.Device(self._client, device_id)

    def is_available(self, device_id):
        """Return device availability."""
        return device_id in self._client.device_ids


class TelldusLiveEntity(Entity):
    """Base class for all Telldus Live entities."""

    def __init__(self, hass, device_id):
        """Initialize the entity."""
        self._id = device_id
        self._client = hass.data[DOMAIN]
        self._client.entities.append(self)
        _LOGGER.debug('Created device %s', self)

    def changed(self):
        """A property of the device might have changed."""
        self.schedule_update_ha_state()

    @property
    def device_id(self):
        """Return the id of the device."""
        return self._id

    @property
    def device(self):
        """Return the representaion of the device."""
        return self._client.device(self.device_id)

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

    @property
    def should_poll(self):
        """Polling is not needed."""
        return False

    @property
    def assumed_state(self):
        """Return true if unable to access real state of entity."""
        return True

    @property
    def name(self):
        """Return name of device."""
        return self.device.name or DEVICE_DEFAULT_NAME

    @property
    def available(self):
        """Return true if device is not offline."""
        return self._client.is_available(self.device_id)

    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        attrs = {}
        if self._battery_level:
            attrs[ATTR_BATTERY_LEVEL] = self._battery_level
        if self._last_updated:
            attrs[ATTR_LAST_UPDATED] = self._last_updated
        return attrs

    @property
    def _battery_level(self):
        """Return the battery level of a device."""
        return round(self.device.battery * 100 / 255) \
            if self.device.battery else None

    @property
    def _last_updated(self):
        """Return the last update of a device."""
        return str(datetime.fromtimestamp(self.device.last_updated)) \
            if self.device.last_updated else None
