"""The tests for the Xiaomi router device tracker platform."""
import logging
import unittest
from unittest import mock
from unittest.mock import patch

import requests

from homeassistant.components.device_tracker import DOMAIN, xiaomi as xiaomi
from homeassistant.components.device_tracker.xiaomi import get_scanner
from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD,
                                 CONF_PLATFORM)
from tests.common import get_test_home_assistant

_LOGGER = logging.getLogger(__name__)

INVALID_USERNAME = 'bob'
TOKEN_TIMEOUT_USERNAME = 'tok'
URL_AUTHORIZE = 'http://192.168.0.1/cgi-bin/luci/api/xqsystem/login'
URL_LIST_END = 'api/misystem/devicelist'

FIRST_CALL = True


def mocked_requests(*args, **kwargs):
    """Mock requests.get invocations."""
    class MockResponse:
        """Class to represent a mocked response."""

        def __init__(self, json_data, status_code):
            """Initialize the mock response class."""
            self.json_data = json_data
            self.status_code = status_code

        def json(self):
            """Return the json of the response."""
            return self.json_data

        @property
        def content(self):
            """Return the content of the response."""
            return self.json()

        def raise_for_status(self):
            """Raise an HTTPError if status is not 200."""
            if self.status_code != 200:
                raise requests.HTTPError(self.status_code)

    data = kwargs.get('data')
    global FIRST_CALL

    if data and data.get('username', None) == INVALID_USERNAME:
        # deliver an invalid token
        return MockResponse({
            "code": "401",
            "msg": "Invalid token"
        }, 200)
    elif data and data.get('username', None) == TOKEN_TIMEOUT_USERNAME:
        # deliver an expired token
        return MockResponse({
            "url": "/cgi-bin/luci/;stok=ef5860/web/home",
            "token": "timedOut",
            "code": "0"
        }, 200)
    elif str(args[0]).startswith(URL_AUTHORIZE):
        # deliver an authorized token
        return MockResponse({
            "url": "/cgi-bin/luci/;stok=ef5860/web/home",
            "token": "ef5860",
            "code": "0"
        }, 200)
    elif str(args[0]).endswith("timedOut/" + URL_LIST_END) \
            and FIRST_CALL is True:
        FIRST_CALL = False
        # deliver an error when called with expired token
        return MockResponse({
            "code": "401",
            "msg": "Invalid token"
        }, 200)
    elif str(args[0]).endswith(URL_LIST_END):
        # deliver the device list
        return MockResponse({
            "mac": "1C:98:EC:0E:D5:A4",
            "list": [
                {
                    "mac": "23:83:BF:F6:38:A0",
                    "oname": "12255ff",
                    "isap": 0,
                    "parent": "",
                    "authority": {
                        "wan": 1,
                        "pridisk": 0,
                        "admin": 1,
                        "lan": 0
                    },
                    "push": 0,
                    "online": 1,
                    "name": "Device1",
                    "times": 0,
                    "ip": [
                        {
                            "downspeed": "0",
                            "online": "496957",
                            "active": 1,
                            "upspeed": "0",
                            "ip": "192.168.0.25"
                        }
                    ],
                    "statistics": {
                        "downspeed": "0",
                        "online": "496957",
                        "upspeed": "0"
                    },
                    "icon": "",
                    "type": 1
                },
                {
                    "mac": "1D:98:EC:5E:D5:A6",
                    "oname": "CdddFG58",
                    "isap": 0,
                    "parent": "",
                    "authority": {
                        "wan": 1,
                        "pridisk": 0,
                        "admin": 1,
                        "lan": 0
                    },
                    "push": 0,
                    "online": 1,
                    "name": "Device2",
                    "times": 0,
                    "ip": [
                        {
                            "downspeed": "0",
                            "online": "347325",
                            "active": 1,
                            "upspeed": "0",
                            "ip": "192.168.0.3"
                        }
                    ],
                    "statistics": {
                        "downspeed": "0",
                        "online": "347325",
                        "upspeed": "0"
                    },
                    "icon": "",
                    "type": 0
                },
            ],
            "code": 0
        }, 200)
    else:
        _LOGGER.debug('UNKNOWN ROUTE')


class TestXiaomiDeviceScanner(unittest.TestCase):
    """Xiaomi device scanner test class."""

    def setUp(self):
        """Initialize values for this testcase class."""
        self.hass = get_test_home_assistant()

    def tearDown(self):
        """Stop everything that was started."""
        self.hass.stop()

    @mock.patch(
        'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner',
        return_value=mock.MagicMock())
    def test_config(self, xiaomi_mock):
        """Testing minimal configuration."""
        config = {
            DOMAIN: xiaomi.PLATFORM_SCHEMA({
                CONF_PLATFORM: xiaomi.DOMAIN,
                CONF_HOST: '192.168.0.1',
                CONF_PASSWORD: 'passwordTest'
            })
        }
        xiaomi.get_scanner(self.hass, config)
        self.assertEqual(xiaomi_mock.call_count, 1)
        self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN]))
        call_arg = xiaomi_mock.call_args[0][0]
        self.assertEqual(call_arg['username'], 'admin')
        self.assertEqual(call_arg['password'], 'passwordTest')
        self.assertEqual(call_arg['host'], '192.168.0.1')
        self.assertEqual(call_arg['platform'], 'device_tracker')

    @mock.patch(
        'homeassistant.components.device_tracker.xiaomi.XiaomiDeviceScanner',
        return_value=mock.MagicMock())
    def test_config_full(self, xiaomi_mock):
        """Testing full configuration."""
        config = {
            DOMAIN: xiaomi.PLATFORM_SCHEMA({
                CONF_PLATFORM: xiaomi.DOMAIN,
                CONF_HOST: '192.168.0.1',
                CONF_USERNAME: 'alternativeAdminName',
                CONF_PASSWORD: 'passwordTest'
            })
        }
        xiaomi.get_scanner(self.hass, config)
        self.assertEqual(xiaomi_mock.call_count, 1)
        self.assertEqual(xiaomi_mock.call_args, mock.call(config[DOMAIN]))
        call_arg = xiaomi_mock.call_args[0][0]
        self.assertEqual(call_arg['username'], 'alternativeAdminName')
        self.assertEqual(call_arg['password'], 'passwordTest')
        self.assertEqual(call_arg['host'], '192.168.0.1')
        self.assertEqual(call_arg['platform'], 'device_tracker')

    @patch('requests.get', side_effect=mocked_requests)
    @patch('requests.post', side_effect=mocked_requests)
    def test_invalid_credential(self, mock_get, mock_post):
        """"Testing invalid credential handling."""
        config = {
            DOMAIN: xiaomi.PLATFORM_SCHEMA({
                CONF_PLATFORM: xiaomi.DOMAIN,
                CONF_HOST: '192.168.0.1',
                CONF_USERNAME: INVALID_USERNAME,
                CONF_PASSWORD: 'passwordTest'
            })
        }
        self.assertIsNone(get_scanner(self.hass, config))

    @patch('requests.get', side_effect=mocked_requests)
    @patch('requests.post', side_effect=mocked_requests)
    def test_valid_credential(self, mock_get, mock_post):
        """"Testing valid refresh."""
        config = {
            DOMAIN: xiaomi.PLATFORM_SCHEMA({
                CONF_PLATFORM: xiaomi.DOMAIN,
                CONF_HOST: '192.168.0.1',
                CONF_USERNAME: 'admin',
                CONF_PASSWORD: 'passwordTest'
            })
        }
        scanner = get_scanner(self.hass, config)
        self.assertIsNotNone(scanner)
        self.assertEqual(2, len(scanner.scan_devices()))
        self.assertEqual("Device1",
                         scanner.get_device_name("23:83:BF:F6:38:A0"))
        self.assertEqual("Device2",
                         scanner.get_device_name("1D:98:EC:5E:D5:A6"))

    @patch('requests.get', side_effect=mocked_requests)
    @patch('requests.post', side_effect=mocked_requests)
    def test_token_timed_out(self, mock_get, mock_post):
        """"Testing refresh with a timed out token.

        New token is requested and list is downloaded a second time.
        """
        config = {
            DOMAIN: xiaomi.PLATFORM_SCHEMA({
                CONF_PLATFORM: xiaomi.DOMAIN,
                CONF_HOST: '192.168.0.1',
                CONF_USERNAME: TOKEN_TIMEOUT_USERNAME,
                CONF_PASSWORD: 'passwordTest'
            })
        }
        scanner = get_scanner(self.hass, config)
        self.assertIsNotNone(scanner)
        self.assertEqual(2, len(scanner.scan_devices()))
        self.assertEqual("Device1",
                         scanner.get_device_name("23:83:BF:F6:38:A0"))
        self.assertEqual("Device2",
                         scanner.get_device_name("1D:98:EC:5E:D5:A6"))