"""
Support Hook, available at hooksmarthome.com.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.hook/
"""
import logging
import asyncio

import voluptuous as vol
import async_timeout
import aiohttp

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, CONF_TOKEN
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

HOOK_ENDPOINT = 'https://api.gethook.io/v1/'
TIMEOUT = 10

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Exclusive(CONF_PASSWORD, 'hook_secret', msg='hook: provide ' +
                  'username/password OR token'): cv.string,
    vol.Exclusive(CONF_TOKEN, 'hook_secret', msg='hook: provide ' +
                  'username/password OR token'): cv.string,
    vol.Inclusive(CONF_USERNAME, 'hook_auth'): cv.string,
    vol.Inclusive(CONF_PASSWORD, 'hook_auth'): cv.string,
})


@asyncio.coroutine
def async_setup_platform(hass, config, async_add_entities,
                         discovery_info=None):
    """Set up Hook by getting the access token and list of actions."""
    username = config.get(CONF_USERNAME)
    password = config.get(CONF_PASSWORD)
    token = config.get(CONF_TOKEN)
    websession = async_get_clientsession(hass)
    # If password is set in config, prefer it over token
    if username is not None and password is not None:
        try:
            with async_timeout.timeout(TIMEOUT, loop=hass.loop):
                response = yield from websession.post(
                    '{}{}'.format(HOOK_ENDPOINT, 'user/login'),
                    data={
                        'username': username,
                        'password': password})
            # The Hook API returns JSON but calls it 'text/html'.  Setting
            # content_type=None disables aiohttp's content-type validation.
            data = yield from response.json(content_type=None)
        except (asyncio.TimeoutError, aiohttp.ClientError) as error:
            _LOGGER.error("Failed authentication API call: %s", error)
            return False

        try:
            token = data['data']['token']
        except KeyError:
            _LOGGER.error("No token. Check username and password")
            return False

    try:
        with async_timeout.timeout(TIMEOUT, loop=hass.loop):
            response = yield from websession.get(
                '{}{}'.format(HOOK_ENDPOINT, 'device'),
                params={"token": token})
        data = yield from response.json(content_type=None)
    except (asyncio.TimeoutError, aiohttp.ClientError) as error:
        _LOGGER.error("Failed getting devices: %s", error)
        return False

    async_add_entities(
        HookSmartHome(
            hass,
            token,
            d['device_id'],
            d['device_name'])
        for lst in data['data']
        for d in lst)


class HookSmartHome(SwitchDevice):
    """Representation of a Hook device, allowing on and off commands."""

    def __init__(self, hass, token, device_id, device_name):
        """Initialize the switch."""
        self.hass = hass
        self._token = token
        self._state = False
        self._id = device_id
        self._name = device_name
        _LOGGER.debug(
            "Creating Hook object: ID: %s  Name: %s", self._id, self._name)

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

    @property
    def is_on(self):
        """Return true if device is on."""
        return self._state

    @asyncio.coroutine
    def _send(self, url):
        """Send the url to the Hook API."""
        try:
            _LOGGER.debug("Sending: %s", url)
            websession = async_get_clientsession(self.hass)
            with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
                response = yield from websession.get(
                    url, params={"token": self._token})
            data = yield from response.json(content_type=None)

        except (asyncio.TimeoutError, aiohttp.ClientError) as error:
            _LOGGER.error("Failed setting state: %s", error)
            return False

        _LOGGER.debug("Got: %s", data)
        return data['return_value'] == '1'

    @asyncio.coroutine
    def async_turn_on(self, **kwargs):
        """Turn the device on asynchronously."""
        _LOGGER.debug("Turning on: %s", self._name)
        url = '{}{}{}{}'.format(
            HOOK_ENDPOINT, 'device/trigger/', self._id, '/On')
        success = yield from self._send(url)
        self._state = success

    @asyncio.coroutine
    def async_turn_off(self, **kwargs):
        """Turn the device off asynchronously."""
        _LOGGER.debug("Turning off: %s", self._name)
        url = '{}{}{}{}'.format(
            HOOK_ENDPOINT, 'device/trigger/', self._id, '/Off')
        success = yield from self._send(url)
        # If it wasn't successful, keep state as true
        self._state = not success