"""
Support for August binary sensors.

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

from homeassistant.components.august import DATA_AUGUST
from homeassistant.components.binary_sensor import (BinarySensorDevice)

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['august']

SCAN_INTERVAL = timedelta(seconds=5)


def _retrieve_door_state(data, lock):
    """Get the latest state of the DoorSense sensor."""
    return data.get_door_state(lock.device_id)


def _retrieve_online_state(data, doorbell):
    """Get the latest state of the sensor."""
    detail = data.get_doorbell_detail(doorbell.device_id)
    if detail is None:
        return None

    return detail.is_online


def _retrieve_motion_state(data, doorbell):
    from august.activity import ActivityType
    return _activity_time_based_state(data, doorbell,
                                      [ActivityType.DOORBELL_MOTION,
                                       ActivityType.DOORBELL_DING])


def _retrieve_ding_state(data, doorbell):
    from august.activity import ActivityType
    return _activity_time_based_state(data, doorbell,
                                      [ActivityType.DOORBELL_DING])


def _activity_time_based_state(data, doorbell, activity_types):
    """Get the latest state of the sensor."""
    latest = data.get_latest_device_activity(doorbell.device_id,
                                             *activity_types)

    if latest is not None:
        start = latest.activity_start_time
        end = latest.activity_end_time + timedelta(seconds=30)
        return start <= datetime.now() <= end
    return None


# Sensor types: Name, device_class, state_provider
SENSOR_TYPES_DOOR = {
    'door_open': ['Open', 'door', _retrieve_door_state],
}

SENSOR_TYPES_DOORBELL = {
    'doorbell_ding': ['Ding', 'occupancy', _retrieve_ding_state],
    'doorbell_motion': ['Motion', 'motion', _retrieve_motion_state],
    'doorbell_online': ['Online', 'connectivity', _retrieve_online_state],
}


def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the August binary sensors."""
    data = hass.data[DATA_AUGUST]
    devices = []

    from august.lock import LockDoorStatus
    for door in data.locks:
        for sensor_type in SENSOR_TYPES_DOOR:
            state_provider = SENSOR_TYPES_DOOR[sensor_type][2]
            if state_provider(data, door) is LockDoorStatus.UNKNOWN:
                _LOGGER.debug(
                    "Not adding sensor class %s for lock %s ",
                    SENSOR_TYPES_DOOR[sensor_type][1], door.device_name
                )
                continue

            _LOGGER.debug(
                "Adding sensor class %s for %s",
                SENSOR_TYPES_DOOR[sensor_type][1], door.device_name
            )
            devices.append(AugustDoorBinarySensor(data, sensor_type, door))

    for doorbell in data.doorbells:
        for sensor_type in SENSOR_TYPES_DOORBELL:
            _LOGGER.debug("Adding doorbell sensor class %s for %s",
                          SENSOR_TYPES_DOORBELL[sensor_type][1],
                          doorbell.device_name)
            devices.append(
                AugustDoorbellBinarySensor(data, sensor_type,
                                           doorbell)
            )

    add_entities(devices, True)


class AugustDoorBinarySensor(BinarySensorDevice):
    """Representation of an August Door binary sensor."""

    def __init__(self, data, sensor_type, door):
        """Initialize the sensor."""
        self._data = data
        self._sensor_type = sensor_type
        self._door = door
        self._state = None
        self._available = False

    @property
    def available(self):
        """Return the availability of this sensor."""
        return self._available

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

    @property
    def device_class(self):
        """Return the class of this device, from component DEVICE_CLASSES."""
        return SENSOR_TYPES_DOOR[self._sensor_type][1]

    @property
    def name(self):
        """Return the name of the binary sensor."""
        return "{} {}".format(self._door.device_name,
                              SENSOR_TYPES_DOOR[self._sensor_type][0])

    def update(self):
        """Get the latest state of the sensor."""
        state_provider = SENSOR_TYPES_DOOR[self._sensor_type][2]
        self._state = state_provider(self._data, self._door)
        self._available = self._state is not None

        from august.lock import LockDoorStatus
        self._state = self._state == LockDoorStatus.OPEN


class AugustDoorbellBinarySensor(BinarySensorDevice):
    """Representation of an August binary sensor."""

    def __init__(self, data, sensor_type, doorbell):
        """Initialize the sensor."""
        self._data = data
        self._sensor_type = sensor_type
        self._doorbell = doorbell
        self._state = None
        self._available = False

    @property
    def available(self):
        """Return the availability of this sensor."""
        return self._available

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

    @property
    def device_class(self):
        """Return the class of this device, from component DEVICE_CLASSES."""
        return SENSOR_TYPES_DOORBELL[self._sensor_type][1]

    @property
    def name(self):
        """Return the name of the binary sensor."""
        return "{} {}".format(self._doorbell.device_name,
                              SENSOR_TYPES_DOORBELL[self._sensor_type][0])

    def update(self):
        """Get the latest state of the sensor."""
        state_provider = SENSOR_TYPES_DOORBELL[self._sensor_type][2]
        self._state = state_provider(self._data, self._doorbell)
        self._available = self._state is not None