"""
Integrate with NO-IP Dynamic DNS service.

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

import aiohttp
from aiohttp.hdrs import USER_AGENT, AUTHORIZATION
import async_timeout
import voluptuous as vol

from homeassistant.const import (
    CONF_DOMAIN, CONF_TIMEOUT, CONF_PASSWORD, CONF_USERNAME)
from homeassistant.helpers.aiohttp_client import SERVER_SOFTWARE
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'no_ip'

# We should set a dedicated address for the user agent.
EMAIL = 'hello@home-assistant.io'

INTERVAL = timedelta(minutes=5)

DEFAULT_TIMEOUT = 10

NO_IP_ERRORS = {
    'nohost': "Hostname supplied does not exist under specified account",
    'badauth': "Invalid username password combination",
    'badagent': "Client disabled",
    '!donator':
        "An update request was sent with a feature that is not available",
    'abuse': "Username is blocked due to abuse",
    '911': "A fatal error on NO-IP's side such as a database outage",
}

UPDATE_URL = 'https://dynupdate.noip.com/nic/update'
HA_USER_AGENT = "{} {}".format(SERVER_SOFTWARE, EMAIL)

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_DOMAIN): cv.string,
        vol.Required(CONF_USERNAME): cv.string,
        vol.Required(CONF_PASSWORD): cv.string,
        vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
    })
}, extra=vol.ALLOW_EXTRA)


@asyncio.coroutine
def async_setup(hass, config):
    """Initialize the NO-IP component."""
    domain = config[DOMAIN].get(CONF_DOMAIN)
    user = config[DOMAIN].get(CONF_USERNAME)
    password = config[DOMAIN].get(CONF_PASSWORD)
    timeout = config[DOMAIN].get(CONF_TIMEOUT)

    auth_str = base64.b64encode('{}:{}'.format(user, password).encode('utf-8'))

    session = hass.helpers.aiohttp_client.async_get_clientsession()

    result = yield from _update_no_ip(
        hass, session, domain, auth_str, timeout)

    if not result:
        return False

    @asyncio.coroutine
    def update_domain_interval(now):
        """Update the NO-IP entry."""
        yield from _update_no_ip(hass, session, domain, auth_str, timeout)

    hass.helpers.event.async_track_time_interval(
        update_domain_interval, INTERVAL)

    return True


@asyncio.coroutine
def _update_no_ip(hass, session, domain, auth_str, timeout):
    """Update NO-IP."""
    url = UPDATE_URL

    params = {
        'hostname': domain,
    }

    headers = {
        AUTHORIZATION: "Basic {}".format(auth_str.decode('utf-8')),
        USER_AGENT: HA_USER_AGENT,
    }

    try:
        with async_timeout.timeout(timeout, loop=hass.loop):
            resp = yield from session.get(url, params=params, headers=headers)
            body = yield from resp.text()

            if body.startswith('good') or body.startswith('nochg'):
                return True

            _LOGGER.warning("Updating NO-IP failed: %s => %s", domain,
                            NO_IP_ERRORS[body.strip()])

    except aiohttp.ClientError:
        _LOGGER.warning("Can't connect to NO-IP API")

    except asyncio.TimeoutError:
        _LOGGER.warning("Timeout from NO-IP API for domain: %s", domain)

    return False