"""Test the cloud component."""
import asyncio
import json
from unittest.mock import patch, MagicMock, mock_open

import pytest

from homeassistant.components import cloud
from homeassistant.util.dt import utcnow

from tests.common import mock_coro


@pytest.fixture
def mock_os():
    """Mock os module."""
    with patch('homeassistant.components.cloud.os') as os:
        os.path.isdir.return_value = True
        yield os


@asyncio.coroutine
def test_constructor_loads_info_from_constant():
    """Test non-dev mode loads info from SERVERS constant."""
    hass = MagicMock(data={})
    with patch.dict(cloud.SERVERS, {
        'beer': {
            'cognito_client_id': 'test-cognito_client_id',
            'user_pool_id': 'test-user_pool_id',
            'region': 'test-region',
            'relayer': 'test-relayer',
        }
    }), patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
              return_value=mock_coro(True)):
        result = yield from cloud.async_setup(hass, {
            'cloud': {cloud.CONF_MODE: 'beer'}
        })
        assert result

    cl = hass.data['cloud']
    assert cl.mode == 'beer'
    assert cl.cognito_client_id == 'test-cognito_client_id'
    assert cl.user_pool_id == 'test-user_pool_id'
    assert cl.region == 'test-region'
    assert cl.relayer == 'test-relayer'


@asyncio.coroutine
def test_constructor_loads_info_from_config():
    """Test non-dev mode loads info from SERVERS constant."""
    hass = MagicMock(data={})

    with patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
               return_value=mock_coro(True)):
        result = yield from cloud.async_setup(hass, {
            'cloud': {
                cloud.CONF_MODE: cloud.MODE_DEV,
                'cognito_client_id': 'test-cognito_client_id',
                'user_pool_id': 'test-user_pool_id',
                'region': 'test-region',
                'relayer': 'test-relayer',
            }
        })
    assert result

    cl = hass.data['cloud']
    assert cl.mode == cloud.MODE_DEV
    assert cl.cognito_client_id == 'test-cognito_client_id'
    assert cl.user_pool_id == 'test-user_pool_id'
    assert cl.region == 'test-region'
    assert cl.relayer == 'test-relayer'


@asyncio.coroutine
def test_initialize_loads_info(mock_os, hass):
    """Test initialize will load info from config file."""
    mock_os.path.isfile.return_value = True
    mopen = mock_open(read_data=json.dumps({
        'id_token': 'test-id-token',
        'access_token': 'test-access-token',
        'refresh_token': 'test-refresh-token',
    }))

    cl = cloud.Cloud(hass, cloud.MODE_DEV, None, None)
    cl.iot = MagicMock()
    cl.iot.connect.return_value = mock_coro()

    with patch('homeassistant.components.cloud.open', mopen, create=True), \
            patch('homeassistant.components.cloud.Cloud._decode_claims'):
        yield from cl.async_start(None)

    assert cl.id_token == 'test-id-token'
    assert cl.access_token == 'test-access-token'
    assert cl.refresh_token == 'test-refresh-token'
    assert len(cl.iot.connect.mock_calls) == 1


@asyncio.coroutine
def test_logout_clears_info(mock_os, hass):
    """Test logging out disconnects and removes info."""
    cl = cloud.Cloud(hass, cloud.MODE_DEV, None, None)
    cl.iot = MagicMock()
    cl.iot.disconnect.return_value = mock_coro()

    yield from cl.logout()

    assert len(cl.iot.disconnect.mock_calls) == 1
    assert cl.id_token is None
    assert cl.access_token is None
    assert cl.refresh_token is None
    assert len(mock_os.remove.mock_calls) == 1


@asyncio.coroutine
def test_write_user_info():
    """Test writing user info works."""
    mopen = mock_open()

    cl = cloud.Cloud(MagicMock(), cloud.MODE_DEV, None, None)
    cl.id_token = 'test-id-token'
    cl.access_token = 'test-access-token'
    cl.refresh_token = 'test-refresh-token'

    with patch('homeassistant.components.cloud.open', mopen, create=True):
        cl.write_user_info()

    handle = mopen()

    assert len(handle.write.mock_calls) == 1
    data = json.loads(handle.write.mock_calls[0][1][0])
    assert data == {
        'access_token': 'test-access-token',
        'id_token': 'test-id-token',
        'refresh_token': 'test-refresh-token',
    }


@asyncio.coroutine
def test_subscription_expired():
    """Test subscription being expired."""
    cl = cloud.Cloud(None, cloud.MODE_DEV, None, None)
    token_val = {
        'custom:sub-exp': '2017-11-13'
    }
    with patch.object(cl, '_decode_claims', return_value=token_val), \
            patch('homeassistant.util.dt.utcnow',
                  return_value=utcnow().replace(year=2018)):
        assert cl.subscription_expired


@asyncio.coroutine
def test_subscription_not_expired():
    """Test subscription not being expired."""
    cl = cloud.Cloud(None, cloud.MODE_DEV, None, None)
    token_val = {
        'custom:sub-exp': '2017-11-13'
    }
    with patch.object(cl, '_decode_claims', return_value=token_val), \
            patch('homeassistant.util.dt.utcnow',
                  return_value=utcnow().replace(year=2017, month=11, day=9)):
        assert not cl.subscription_expired