"""Integration with the Rachio Iro sprinkler system controller."""
import logging
from datetime import timedelta
import voluptuous as vol

import homeassistant.helpers.config_validation as cv
import homeassistant.util as util
from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA
from homeassistant.const import CONF_ACCESS_TOKEN

REQUIREMENTS = ['rachiopy==0.1.2']

_LOGGER = logging.getLogger(__name__)

DATA_RACHIO = 'rachio'

CONF_MANUAL_RUN_MINS = 'manual_run_mins'
DEFAULT_MANUAL_RUN_MINS = 10

MIN_UPDATE_INTERVAL = timedelta(seconds=30)
MIN_FORCED_UPDATE_INTERVAL = timedelta(seconds=1)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_ACCESS_TOKEN): cv.string,
    vol.Optional(CONF_MANUAL_RUN_MINS, default=DEFAULT_MANUAL_RUN_MINS):
        cv.positive_int
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the component."""
    # Get options
    manual_run_mins = config.get(CONF_MANUAL_RUN_MINS)
    _LOGGER.debug("Rachio run time is %d min", manual_run_mins)

    # Get access token
    _LOGGER.debug("Getting Rachio access token...")
    access_token = config.get(CONF_ACCESS_TOKEN)

    # Configure API
    _LOGGER.debug("Configuring Rachio API...")
    from rachiopy import Rachio
    rachio = Rachio(access_token)
    person = None
    try:
        person = _get_person(rachio)
    except KeyError:
        _LOGGER.error("Could not reach the Rachio API. "
                      "Is your access token valid?")
        return False

    # Get and persist devices
    devices = _list_devices(rachio, manual_run_mins)
    if not devices:
        _LOGGER.error("No Rachio devices found in account " +
                      person['username'])
        return False

    hass.data[DATA_RACHIO] = devices[0]

    if len(devices) > 1:
        _LOGGER.warning("Multiple Rachio devices found in account, "
                        "using " + hass.data[DATA_RACHIO].device_id)
    else:
        _LOGGER.info("Found Rachio device")

    hass.data[DATA_RACHIO].update()
    add_devices(hass.data[DATA_RACHIO].list_zones())
    return True


def _get_person(rachio):
    """Pull the account info of the person whose access token was provided."""
    person_id = rachio.person.getInfo()[1]['id']
    return rachio.person.get(person_id)[1]


def _list_devices(rachio, manual_run_mins):
    """Pull a list of devices on the account."""
    return [RachioIro(rachio, d['id'], manual_run_mins)
            for d in _get_person(rachio)['devices']]


class RachioIro(object):
    """Represents one Rachio Iro."""

    def __init__(self, rachio, device_id, manual_run_mins):
        """Initialize a new device."""
        self.rachio = rachio
        self._device_id = device_id
        self.manual_run_mins = manual_run_mins
        self._device = None
        self._running = None
        self._zones = None

    def __str__(self):
        """Display the device as a string."""
        return "Rachio Iro " + self.serial_number

    @property
    def device_id(self):
        """How the Rachio API refers to the device."""
        return self._device['id']

    @property
    def status(self):
        """The current status of the device."""
        return self._device['status']

    @property
    def serial_number(self):
        """The serial number of the device."""
        return self._device['serialNumber']

    @property
    def is_paused(self):
        """Whether the device is temporarily disabled."""
        return self._device['paused']

    @property
    def is_on(self):
        """Whether the device is powered on and connected."""
        return self._device['on']

    @property
    def current_schedule(self):
        """The schedule that the device is running right now."""
        return self._running

    def list_zones(self, include_disabled=False):
        """A list of the zones connected to the device and their data."""
        if not self._zones:
            self._zones = [RachioZone(self.rachio, self, zone['id'],
                                      self.manual_run_mins)
                           for zone in self._device['zones']]

        if include_disabled:
            return self._zones

        self.update(no_throttle=True)
        return [z for z in self._zones if z.is_enabled]

    @util.Throttle(MIN_UPDATE_INTERVAL, MIN_FORCED_UPDATE_INTERVAL)
    def update(self, **kwargs):
        """Pull updated device info from the Rachio API."""
        self._device = self.rachio.device.get(self._device_id)[1]
        self._running = self.rachio.device\
                            .getCurrentSchedule(self._device_id)[1]

        # Possibly update all zones
        for zone in self.list_zones(include_disabled=True):
            zone.update()

        _LOGGER.debug("Updated %s", str(self))


class RachioZone(SwitchDevice):
    """Represents one zone of sprinklers connected to the Rachio Iro."""

    def __init__(self, rachio, device, zone_id, manual_run_mins):
        """Initialize a new Rachio Zone."""
        self.rachio = rachio
        self._device = device
        self._zone_id = zone_id
        self._zone = None
        self._manual_run_secs = manual_run_mins * 60

    def __str__(self):
        """Display the zone as a string."""
        return "Rachio Zone " + self.name

    @property
    def zone_id(self):
        """How the Rachio API refers to the zone."""
        return self._zone['id']

    @property
    def unique_id(self):
        """Generate a unique string ID for the zone."""
        return '{iro}-{zone}'.format(
            iro=self._device.device_id,
            zone=self.zone_id)

    @property
    def number(self):
        """The physical connection of the zone pump."""
        return self._zone['zoneNumber']

    @property
    def name(self):
        """The friendly name of the zone."""
        return self._zone['name']

    @property
    def is_enabled(self):
        """Whether the zone is allowed to run."""
        return self._zone['enabled']

    @property
    def is_on(self):
        """Whether the zone is currently running."""
        schedule = self._device.current_schedule
        return self.zone_id == schedule.get('zoneId')

    def update(self):
        """Pull updated zone info from the Rachio API."""
        self._zone = self.rachio.zone.get(self._zone_id)[1]

        # Possibly update device
        self._device.update()

        _LOGGER.debug("Updated %s", str(self))

    def turn_on(self):
        """Start the zone."""
        # Stop other zones first
        self.turn_off()

        _LOGGER.info("Watering %s for %d s", self.name, self._manual_run_secs)
        self.rachio.zone.start(self.zone_id, self._manual_run_secs)

    def turn_off(self):
        """Stop all zones."""
        _LOGGER.info("Stopping watering of all zones")
        self.rachio.device.stopWater(self._device.device_id)
