"""
Support for monitoring an AVM Fritz!Box router.

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

import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_HOST, STATE_UNAVAILABLE)
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle

from requests.exceptions import RequestException

REQUIREMENTS = ['fritzconnection==0.6']

MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)

CONF_DEFAULT_IP = '169.254.1.1'  # This IP is valid for all FRITZ!Box routers.

ATTR_IS_LINKED = "is_linked"
ATTR_IS_CONNECTED = "is_connected"
ATTR_WAN_ACCESS_TYPE = "wan_access_type"
ATTR_EXTERNAL_IP = "external_ip"
ATTR_UPTIME = "uptime"
ATTR_BYTES_SENT = "bytes_sent"
ATTR_BYTES_RECEIVED = "bytes_received"
ATTR_MAX_BYTE_RATE_UP = "max_byte_rate_up"
ATTR_MAX_BYTE_RATE_DOWN = "max_byte_rate_down"

STATE_ONLINE = 'online'
STATE_OFFLINE = 'offline'

ICON = 'mdi:web'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_HOST, default=CONF_DEFAULT_IP): cv.string,
})

_LOGGER = logging.getLogger(__name__)


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the fritzbox monitor sensors."""
    # pylint: disable=import-error
    import fritzconnection as fc
    from fritzconnection.fritzconnection import FritzConnectionException

    host = config[CONF_HOST]

    try:
        fstatus = fc.FritzStatus(address=host)
    except (ValueError, TypeError, FritzConnectionException):
        fstatus = None

    if fstatus is None:
        _LOGGER.error('Failed to establish connection to FRITZ!Box '
                      'with IP: %s', host)
        return 1
    else:
        _LOGGER.info('Successfully connected to FRITZ!Box')

    sensor = FritzboxMonitorSensor(fstatus)
    devices = [sensor]
    add_devices(devices)


class FritzboxMonitorSensor(Entity):
    """Implementation of a fritzbox monitor sensor."""

    def __init__(self, fstatus):
        """Initialize the sensor."""
        self._name = 'fritz_netmonitor'
        self._fstatus = fstatus
        self._state = STATE_UNAVAILABLE
        self.update()

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

    @property
    def icon(self):
        """Icon to use in the frontend, if any."""
        return ICON

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

    @property
    def state_attributes(self):
        """Return the device state attributes."""
        # Don't return attributes if FritzBox is unreachable
        if self._state == STATE_UNAVAILABLE:
            return {}
        attr = {
            ATTR_IS_LINKED: self._is_linked,
            ATTR_IS_CONNECTED: self._is_connected,
            ATTR_WAN_ACCESS_TYPE: self._wan_access_type,
            ATTR_EXTERNAL_IP: self._external_ip,
            ATTR_UPTIME: self._uptime,
            ATTR_BYTES_SENT: self._bytes_sent,
            ATTR_BYTES_RECEIVED: self._bytes_received,
            ATTR_MAX_BYTE_RATE_UP: self._max_byte_rate_up,
            ATTR_MAX_BYTE_RATE_DOWN: self._max_byte_rate_down,
        }
        return attr

    @Throttle(MIN_TIME_BETWEEN_UPDATES)
    def update(self):
        """Retrieve information from the FritzBox."""
        try:
            self._is_linked = self._fstatus.is_linked
            self._is_connected = self._fstatus.is_connected
            self._wan_access_type = self._fstatus.wan_access_type
            self._external_ip = self._fstatus.external_ip
            self._uptime = self._fstatus.uptime
            self._bytes_sent = self._fstatus.bytes_sent
            self._bytes_received = self._fstatus.bytes_received
            self._max_byte_rate_up = self._fstatus.max_byte_rate[0]
            self._max_byte_rate_down = self._fstatus.max_byte_rate[1]
            self._state = STATE_ONLINE if self._is_connected else STATE_OFFLINE
        except RequestException as err:
            self._state = STATE_UNAVAILABLE
            _LOGGER.warning('Could not reach Fritzbox: %s', err)
