"""The tests for the Demo vacuum platform."""
import unittest

from homeassistant.components import vacuum
from homeassistant.components.vacuum import (
    ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON, ATTR_STATUS,
    ATTR_FAN_SPEED, mqtt)
from homeassistant.components.mqtt import CONF_COMMAND_TOPIC
from homeassistant.const import (
    CONF_PLATFORM, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, CONF_NAME)
from homeassistant.setup import setup_component

from tests.common import (
    fire_mqtt_message, get_test_home_assistant, mock_mqtt_component)
from tests.components.vacuum import common


class TestVacuumMQTT(unittest.TestCase):
    """MQTT vacuum component test class."""

    def setUp(self):  # pylint: disable=invalid-name
        """Set up things to be run when tests are started."""
        self.hass = get_test_home_assistant()
        self.mock_publish = mock_mqtt_component(self.hass)

        self.default_config = {
            CONF_PLATFORM: 'mqtt',
            CONF_NAME: 'mqtttest',
            CONF_COMMAND_TOPIC: 'vacuum/command',
            mqtt.CONF_SEND_COMMAND_TOPIC: 'vacuum/send_command',
            mqtt.CONF_BATTERY_LEVEL_TOPIC: 'vacuum/state',
            mqtt.CONF_BATTERY_LEVEL_TEMPLATE:
                '{{ value_json.battery_level }}',
            mqtt.CONF_CHARGING_TOPIC: 'vacuum/state',
            mqtt.CONF_CHARGING_TEMPLATE: '{{ value_json.charging }}',
            mqtt.CONF_CLEANING_TOPIC: 'vacuum/state',
            mqtt.CONF_CLEANING_TEMPLATE: '{{ value_json.cleaning }}',
            mqtt.CONF_DOCKED_TOPIC: 'vacuum/state',
            mqtt.CONF_DOCKED_TEMPLATE: '{{ value_json.docked }}',
            mqtt.CONF_STATE_TOPIC: 'vacuum/state',
            mqtt.CONF_STATE_TEMPLATE: '{{ value_json.state }}',
            mqtt.CONF_FAN_SPEED_TOPIC: 'vacuum/state',
            mqtt.CONF_FAN_SPEED_TEMPLATE: '{{ value_json.fan_speed }}',
            mqtt.CONF_SET_FAN_SPEED_TOPIC: 'vacuum/set_fan_speed',
            mqtt.CONF_FAN_SPEED_LIST: ['min', 'medium', 'high', 'max'],
        }

    def tearDown(self):  # pylint: disable=invalid-name
        """Stop down everything that was started."""
        self.hass.stop()

    def test_default_supported_features(self):
        """Test that the correct supported features."""
        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })
        entity = self.hass.states.get('vacuum.mqtttest')
        entity_features = \
            entity.attributes.get(mqtt.CONF_SUPPORTED_FEATURES, 0)
        assert sorted(mqtt.services_to_strings(entity_features)) == \
            sorted(['turn_on', 'turn_off', 'stop',
                    'return_home', 'battery', 'status',
                    'clean_spot'])

    def test_all_commands(self):
        """Test simple commands to the vacuum."""
        self.default_config[mqtt.CONF_SUPPORTED_FEATURES] = \
            mqtt.services_to_strings(mqtt.ALL_SERVICES)

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        common.turn_on(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'turn_on', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.turn_off(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'turn_off', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.stop(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'stop', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.clean_spot(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'clean_spot', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.locate(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'locate', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.start_pause(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'start_pause', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.return_to_base(self.hass, 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/command', 'return_to_base', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.set_fan_speed(self.hass, 'high', 'vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/set_fan_speed', 'high', 0, False)
        self.mock_publish.async_publish.reset_mock()

        common.send_command(self.hass, '44 FE 93', entity_id='vacuum.mqtttest')
        self.hass.block_till_done()
        self.mock_publish.async_publish.assert_called_once_with(
            'vacuum/send_command', '44 FE 93', 0, False)

    def test_status(self):
        """Test status updates from the vacuum."""
        self.default_config[mqtt.CONF_SUPPORTED_FEATURES] = \
            mqtt.services_to_strings(mqtt.ALL_SERVICES)

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        message = """{
            "battery_level": 54,
            "cleaning": true,
            "docked": false,
            "charging": false,
            "fan_speed": "max"
        }"""
        fire_mqtt_message(self.hass, 'vacuum/state', message)
        self.hass.block_till_done()
        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_ON == state.state
        assert 'mdi:battery-50' == \
            state.attributes.get(ATTR_BATTERY_ICON)
        assert 54 == state.attributes.get(ATTR_BATTERY_LEVEL)
        assert 'max' == state.attributes.get(ATTR_FAN_SPEED)

        message = """{
            "battery_level": 61,
            "docked": true,
            "cleaning": false,
            "charging": true,
            "fan_speed": "min"
        }"""

        fire_mqtt_message(self.hass, 'vacuum/state', message)
        self.hass.block_till_done()
        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_OFF == state.state
        assert 'mdi:battery-charging-60' == \
            state.attributes.get(ATTR_BATTERY_ICON)
        assert 61 == state.attributes.get(ATTR_BATTERY_LEVEL)
        assert 'min' == state.attributes.get(ATTR_FAN_SPEED)

    def test_battery_template(self):
        """Test that you can use non-default templates for battery_level."""
        self.default_config.update({
            mqtt.CONF_SUPPORTED_FEATURES:
                mqtt.services_to_strings(mqtt.ALL_SERVICES),
            mqtt.CONF_BATTERY_LEVEL_TOPIC: "retroroomba/battery_level",
            mqtt.CONF_BATTERY_LEVEL_TEMPLATE: "{{ value }}"
        })

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        fire_mqtt_message(self.hass, 'retroroomba/battery_level', '54')
        self.hass.block_till_done()
        state = self.hass.states.get('vacuum.mqtttest')
        assert 54 == state.attributes.get(ATTR_BATTERY_LEVEL)
        assert state.attributes.get(ATTR_BATTERY_ICON) == \
            'mdi:battery-50'

    def test_status_invalid_json(self):
        """Test to make sure nothing breaks if the vacuum sends bad JSON."""
        self.default_config[mqtt.CONF_SUPPORTED_FEATURES] = \
            mqtt.services_to_strings(mqtt.ALL_SERVICES)

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        fire_mqtt_message(self.hass, 'vacuum/state', '{"asdfasas false}')
        self.hass.block_till_done()
        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_OFF == state.state
        assert "Stopped" == state.attributes.get(ATTR_STATUS)

    def test_default_availability_payload(self):
        """Test availability by default payload with defined topic."""
        self.default_config.update({
            'availability_topic': 'availability-topic'
        })

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE == state.state

        fire_mqtt_message(self.hass, 'availability-topic', 'online')
        self.hass.block_till_done()

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE != state.state

        fire_mqtt_message(self.hass, 'availability-topic', 'offline')
        self.hass.block_till_done()

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE == state.state

    def test_custom_availability_payload(self):
        """Test availability by custom payload with defined topic."""
        self.default_config.update({
            'availability_topic': 'availability-topic',
            'payload_available': 'good',
            'payload_not_available': 'nogood'
        })

        assert setup_component(self.hass, vacuum.DOMAIN, {
            vacuum.DOMAIN: self.default_config,
        })

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE == state.state

        fire_mqtt_message(self.hass, 'availability-topic', 'good')
        self.hass.block_till_done()

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE != state.state

        fire_mqtt_message(self.hass, 'availability-topic', 'nogood')
        self.hass.block_till_done()

        state = self.hass.states.get('vacuum.mqtttest')
        assert STATE_UNAVAILABLE == state.state
