from datetime import datetime

import pytest
from devolo_home_control_api.backend import MESSAGE_TYPES


@pytest.mark.usefixtures("mock_inspect_devices_metering_plug")
@pytest.mark.usefixtures("home_control_instance")
@pytest.mark.usefixtures("mock_mydevolo__call")
class TestHomeControl:

    def test_hasattr(self):
        homecontrol_functions = (func for func in dir(self.homecontrol)
                                 if callable(getattr(self.homecontrol,
                                                     func)) and not func.startswith("__"))
        assert set(MESSAGE_TYPES.values()).difference(homecontrol_functions) == {"_gateway_accessible",
                                                                                 "_device_state"}

    def test_binary_sensor_devices(self):
        assert hasattr(self.homecontrol.binary_sensor_devices[0], "binary_sensor_property")

    def test_binary_switch_devices(self):
        assert hasattr(self.homecontrol.binary_switch_devices[0], "binary_switch_property")

    def test_blinds_devices(self):
        assert hasattr(self.homecontrol.blinds_devices[0], "multi_level_switch_property")

    def test_multi_level_sensor_devices(self):
        assert hasattr(self.homecontrol.multi_level_sensor_devices[0], "multi_level_sensor_property")

    def test_multi_level_switch_devices(self):
        assert hasattr(self.homecontrol.multi_level_switch_devices[0], "multi_level_switch_property")

    def test_remote_control_devices(self):
        assert hasattr(self.homecontrol.remote_control_devices[0], "remote_control_property")

    def test_get_publisher(self):
        assert len(self.homecontrol.publisher._events) == 10

    def test__automatic_calibration(self):
        device = self.devices['blinds']
        uid = device['uid']
        self.homecontrol._automatic_calibration({
            "UID": f"acs.{uid}",
            "properties": {
                "calibrationStatus": device['calibrationStatus']
            }
        })
        assert self.homecontrol.devices.get(uid).settings_property['automatic_calibration'].calibration_status == \
            bool(device['calibrationStatus'])

    def test___binary_sync(self):
        device = self.devices['blinds']
        uid = device['uid']
        self.homecontrol._binary_sync({
            "UID": f"acs.{uid}",
            "properties": {
                "value": device['inverted']
            }
        })
        assert self.homecontrol.devices.get(uid).settings_property['movement_direction'].inverted is device['inverted']

    def test__binary_async_blinds(self):
        device = self.devices.get("blinds").get("uid")
        i2 = self.devices.get("blinds").get("i2")
        self.homecontrol._binary_async({
            "UID": f"bas.{device}#i2",
            "properties": {
                "value": not i2
            }
        })
        assert self.homecontrol.devices.get(device).settings_property.get("i2").value is not i2

    def test__binary_async_siren(self):
        device = self.devices.get("siren").get("uid")
        muted = self.devices.get("siren").get("muted")
        self.homecontrol._binary_async({
            "UID": f"bas.{device}",
            "properties": {
                "value": not muted
            }
        })
        assert self.homecontrol.devices.get(device).settings_property.get("muted").value is not muted

    def test__binary_sensor(self):
        device = self.devices.get("sensor").get("uid")
        del self.homecontrol.devices[device].binary_sensor_property
        assert not hasattr(self.homecontrol.devices.get(device), "binary_sensor_property")
        self.homecontrol._binary_sensor({
            "UID": self.devices.get("sensor").get("elementUIDs")[0],
            "properties": {
                "state": self.devices.get("sensor").get("state"),
                "sensorType": self.devices.get("sensor").get("sensor_type"),
                "subType": ""
            },
        })
        assert hasattr(self.homecontrol.devices.get(device), "binary_sensor_property")

    def test__binary_switch(self):
        device = self.devices['mains']['uid']
        del self.homecontrol.devices[device].binary_switch_property
        assert not hasattr(self.homecontrol.devices[device], "binary_switch_property")
        self.homecontrol._binary_switch({
            "UID": self.devices['mains']['properties']['elementUIDs'][1],
            "properties": self.devices['mains']['properties'],
        })
        assert hasattr(self.homecontrol.devices[device], "binary_switch_property")

    def test__consumption(self):
        # TODO: Use test data
        device = self.devices.get("mains").get("uid")
        del self.homecontrol.devices[device].consumption_property
        assert not hasattr(self.homecontrol.devices.get(device), "consumption_property")
        self.homecontrol._meter({
            "UID": "devolo.Meter:hdm:ZWave:F6BF9812/2",
            "properties": {
                "currentValue": self.devices.get("mains").get("current_consumption"),
                "totalValue": self.devices.get("mains").get("total_consumption"),
                "sinceTime": self.devices.get("mains").get("properties").get("total_consumption"),
            }
        })
        assert hasattr(self.homecontrol.devices.get(device), "consumption_property")

    def test__general_device(self):
        device = self.devices['mains']
        element_uid = f"gds.{device['uid']}"
        self.homecontrol.devices[device['uid']].settings_property['general_device_settings'].events_enabled = \
            not device['properties']['eventsEnabled']
        self.homecontrol._general_device({
            "UID": element_uid,
            "properties": {
                "settings": {
                    "eventsEnabled": device['properties']['eventsEnabled'],
                    "name": device['properties']['itemName'],
                    "zoneID": device['properties']['zoneId'],
                    "icon": device['properties']['icon'],
                }
            }
        })
        assert self.homecontrol.devices[device['uid']].settings_property['general_device_settings'].events_enabled == \
            device['properties']['eventsEnabled']

    def test__humidity_bar(self):
        # TODO: Use test data
        device = self.devices.get("humidity").get("uid")
        del self.homecontrol.devices[device].humidity_bar_property
        assert not hasattr(self.homecontrol.devices.get(device), "humidity_bar_property")
        self.homecontrol._humidity_bar({
            "UID": f"devolo.HumidityBarValue:{device}",
            "properties": {
                "sensorType": "humidityBarPos",
                "value": 75,
            }
        })
        self.homecontrol._humidity_bar({
            "UID": f"devolo.HumidityBarZone:{device}",
            "properties": {
                "sensorType": "humidityBarZone",
                "value": 1,
            }
        })
        assert self.homecontrol.devices.get(device).humidity_bar_property.get(f"devolo.HumidityBar:{device}").value == 75
        assert self.homecontrol.devices.get(device).humidity_bar_property.get(f"devolo.HumidityBar:{device}").zone == 1

    def test__last_activity_binary_sensor(self):
        device = self.devices['sensor']['uid']
        element_uids = self.devices['sensor']['elementUIDs']
        self.homecontrol._binary_sensor({
            "UID": element_uids[0],
            "properties": {
                "state": self.devices['sensor']['state'],
                "sensorType": self.devices['sensor']['sensor_type'],
                "subType": "",
            }
        })
        self.homecontrol._last_activity({
            "UID": element_uids[1],
            "properties": {
                "lastActivityTime": self.devices['sensor']['last_activity'],
            }
        })
        assert self.homecontrol.devices[device].binary_sensor_property.get(element_uids[0]).last_activity == \
            datetime.utcfromtimestamp(self.devices['sensor']['last_activity'] / 1000)

    def test__last_activity_siren(self):
        device = self.devices['siren']['uid']
        element_uids = self.devices['siren']['elementUIDs']
        self.homecontrol._multi_level_switch({
            "UID": element_uids[0],
            "properties": {
                "value": self.devices['siren']['properties']['value'],
                "switchType": self.devices['siren']['switch_type'],
                "max": self.devices['siren']['properties']['max'],
                "min": self.devices['siren']['properties']['min'],
            }
        })
        self.homecontrol._last_activity({
            "UID": element_uids[3],
            "properties": {
                "lastActivityTime": self.devices['siren']['last_activity'],
            }
        })
        assert self.homecontrol.devices[device].multi_level_switch_property[element_uids[0]].last_activity == \
            datetime.utcfromtimestamp(self.devices['siren']['last_activity'] / 1000)

    def test__led(self):
        device = self.devices['mains']
        element_uid = f"lis.{device['UID']}"
        led_setting = device['properties']['led_setting']
        self.homecontrol._led({
            "UID": element_uid,
            "properties": {
                "led": led_setting,
            }
        })
        assert self.homecontrol.devices[device['UID']].settings_property['led'].led_setting == led_setting
        device = self.devices['sensor']
        element_uid = f"vfs.{device['UID']}"
        led_setting = device['led_setting']
        self.homecontrol._led({
            "UID": element_uid,
            "properties": {
                "feedback": led_setting,
            }
        })
        assert self.homecontrol.devices[device['UID']].settings_property['led'].led_setting == led_setting

    def test__multilevel_async(self):
        device = self.devices['blinds']
        uid = device['uid']
        self.homecontrol._multilevel_async({
            "UID": f"mas.{uid}",
            "properties": {
                "itemId": "motorActivity",
                "value": device['motorActivity'],
            }
        })
        assert self.homecontrol.devices[uid].settings_property['motor_activity'].value == device['motorActivity']

    def test__multilevel_async_mains(self):
        device = self.devices['mains']
        uid = device['uid']
        self.homecontrol._multilevel_async({
            "UID": f"mas.{uid}",
            "properties": {
                "itemId": None,
                "value": device['flashMode'],
            }
        })
        assert self.homecontrol.devices[uid].settings_property['flash_mode'].value == device['flashMode']

    def test__multilevel_async_type_error(self):
        with pytest.raises(TypeError):
            device = self.devices['sensor']
            uid = device['uid']
            self.homecontrol._multilevel_async({
                "UID": f"mas.{uid}",
                "properties": {
                    "itemId": None,
                    "value": device['motion_sensitivity'],
                }
            })

    def test__multilevel_sync_sensor(self):
        device = self.devices['sensor']['uid']
        self.homecontrol._multilevel_sync({
            "UID": self.devices['sensor']['settingUIDs'][2],
            "properties": self.devices['sensor']['properties'],
        })
        assert self.homecontrol.devices[device].settings_property['motion_sensitivity'].motion_sensitivity == \
            self.devices['sensor']['properties']['value']

    def test__multilevel_sync_shutter(self):
        device = self.devices['blinds']['uid']
        self.homecontrol._multilevel_sync({
            "UID": f"mss.{device}",
            "properties": {
                "value": self.devices['blinds']['shutter_duration'],
            }
        })
        assert self.homecontrol.devices[device].settings_property['shutter_duration'].shutter_duration == \
            self.devices['blinds']['shutter_duration']

    def test__multilevel_sync_siren(self):
        device = self.devices['siren']['uid']
        self.homecontrol._multilevel_sync({
            "UID": self.devices['siren']['settingUIDs'][0],
            "properties": self.devices['siren']['properties'],
        })
        assert self.homecontrol.devices[device].settings_property['tone'].tone == self.devices['siren']['properties']['value']

    def test__multi_level_sensor(self):
        # TODO: Use test data
        device = self.devices.get("sensor").get("uid")
        del self.homecontrol.devices[device].multi_level_sensor_property
        assert not hasattr(self.homecontrol.devices.get(device), "multi_level_sensor_property")
        self.homecontrol._multi_level_sensor({
            "UID": self.devices.get("sensor").get("elementUIDs")[2],
            "properties": {
                "value": 90.0,
                "unit": "%",
                "sensorType": "light",
            }
        })
        assert hasattr(self.homecontrol.devices.get(device), "multi_level_sensor_property")

    def test__multi_level_switch(self):
        device = self.devices.get("siren").get("uid")
        del self.homecontrol.devices[device].multi_level_switch_property
        assert not hasattr(self.homecontrol.devices.get(device), "multi_level_switch_property")
        self.homecontrol._multi_level_switch({
            "UID": self.devices.get("siren").get("elementUIDs")[0],
            "properties": {
                "state": self.devices.get("multi_level_switch").get("state"),
                "value": self.devices.get("multi_level_switch").get("value"),
                "switchType": self.devices.get("multi_level_switch").get("switch_type"),
                "max": self.devices.get("multi_level_switch").get("max"),
                "min": self.devices.get("multi_level_switch").get("min"),
            }
        })
        assert hasattr(self.homecontrol.devices.get(device), "multi_level_switch_property")

    def test__parameter(self):
        # TODO: Use test data
        device = self.devices.get("mains").get("uid")
        self.homecontrol._parameter({
            "UID": "cps.hdm:ZWave:F6BF9812/2",
            "properties": {
                "paramChanged": False,
            }
        })
        assert hasattr(self.homecontrol.devices.get(device).settings_property.get("param_changed"), "param_changed")

    def test__protection(self):
        # TODO: Use test data
        device = self.devices.get("mains").get("uid")
        self.homecontrol._protection({
            "UID": "ps.hdm:ZWave:F6BF9812/2",
            "properties": {
                "localSwitch": True,
                "remoteSwitch": False,
            }
        })
        assert hasattr(self.homecontrol.devices.get(device).settings_property.get("protection"), "local_switching")
        assert hasattr(self.homecontrol.devices.get(device).settings_property.get("protection"), "remote_switching")

    def test__remote_control(self):
        device = self.devices.get("remote").get("uid")
        element_uid = self.devices.get("remote").get("elementUIDs")[0]
        del self.homecontrol.devices[device].remote_control_property
        assert not hasattr(self.homecontrol.devices.get(device), "remote_control_property")
        self.homecontrol._remote_control({
            "UID": element_uid,
            "properties": {
                "keyCount": self.devices.get("remote").get("key_count"),
                "keyPressed": 0,
                "type": 1,
            }
        })
        assert self.homecontrol.devices.get(device).remote_control_property[element_uid].key_pressed == 0

    def test__switch_type(self):
        device = self.devices['remote']['uid']
        self.homecontrol._switch_type({
            "UID": f"sts.{device}",
            "properties": {
                "switchType": self.devices['remote']['key_count'] / 2,
            }
        })
        assert self.homecontrol.devices[device].settings_property['switch_type'].value == self.devices['remote']['key_count']

    def test__temperature_report(self):
        # TODO: Use test data
        device = self.devices.get("sensor").get("uid")
        self.homecontrol._temperature_report({
            "UID": "trs.hdm:ZWave:F6BF9812/6",
            "properties": {
                "tempReport": True,
                "targetTempReport": False,
            }
        })
        assert hasattr(self.homecontrol.devices.get(device).settings_property.get("temperature_report"), "temp_report")
        assert hasattr(self.homecontrol.devices.get(device).settings_property.get("temperature_report"), "target_temp_report")

    @pytest.mark.usefixtures("mock_inspect_devices")
    def test_device_change_add(self, mocker):
        uids = [self.devices.get(device).get("uid") for device in self.devices]
        uids.append("test_uid")
        spy = mocker.spy(self.homecontrol, "_inspect_devices")
        self.homecontrol.device_change(uids)
        spy.assert_called_once_with(['test_uid'])

    def test_device_change_remove(self):
        uids = [self.devices.get(device).get("uid") for device in self.devices]
        del uids[4]
        self.homecontrol.device_change(uids)
        assert self.devices.get("mains").get("uid") not in self.homecontrol.devices.keys()

    @pytest.mark.usefixtures("mock_extract_data_from_element_uids")
    @pytest.mark.usefixtures("mock_mprmrest_get_all_devices")
    def test__inspect_devices(self):
        uid = self.devices.get("mains").get("uid")
        del self.homecontrol.devices[uid]
        self.homecontrol._inspect_devices(self.homecontrol.get_all_devices())
        try:
            self.homecontrol.devices[uid]
            assert True
        except KeyError:
            assert False
