"""Tests for the Nest config flow."""
import asyncio
from unittest.mock import Mock, patch

from homeassistant import data_entry_flow
from homeassistant.setup import async_setup_component
from homeassistant.components.nest import config_flow, DOMAIN

from tests.common import mock_coro


async def test_abort_if_no_implementation_registered(hass):
    """Test we abort if no implementation is registered."""
    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()

    assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
    assert result['reason'] == 'no_flows'


async def test_abort_if_already_setup(hass):
    """Test we abort if Nest is already setup."""
    flow = config_flow.NestFlowHandler()
    flow.hass = hass

    with patch.object(hass.config_entries, 'async_entries', return_value=[{}]):
        result = await flow.async_step_init()

    assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
    assert result['reason'] == 'already_setup'


async def test_full_flow_implementation(hass):
    """Test registering an implementation and finishing flow works."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    convert_code = Mock(return_value=mock_coro({'access_token': 'yoo'}))
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, convert_code)
    config_flow.register_flow_implementation(
        hass, 'test-other', 'Test Other', None, None)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'init'

    result = await flow.async_step_init({'flow_impl': 'test'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'
    assert result['description_placeholders'] == {
        'url': 'https://example.com',
    }

    result = await flow.async_step_link({'code': '123ABC'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result['data']['tokens'] == {'access_token': 'yoo'}
    assert result['data']['impl_domain'] == 'test'
    assert result['title'] == 'Nest (via Test)'


async def test_not_pick_implementation_if_only_one(hass):
    """Test we allow picking implementation if we have two."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, None)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'


async def test_abort_if_timeout_generating_auth_url(hass):
    """Test we abort if generating authorize url fails."""
    gen_authorize_url = Mock(side_effect=asyncio.TimeoutError)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, None)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
    assert result['reason'] == 'authorize_url_timeout'


async def test_abort_if_exception_generating_auth_url(hass):
    """Test we abort if generating authorize url blows up."""
    gen_authorize_url = Mock(side_effect=ValueError)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, None)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
    assert result['reason'] == 'authorize_url_fail'


async def test_verify_code_timeout(hass):
    """Test verify code timing out."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    convert_code = Mock(side_effect=asyncio.TimeoutError)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, convert_code)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'

    result = await flow.async_step_link({'code': '123ABC'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'
    assert result['errors'] == {'code': 'timeout'}


async def test_verify_code_invalid(hass):
    """Test verify code invalid."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    convert_code = Mock(side_effect=config_flow.CodeInvalid)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, convert_code)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'

    result = await flow.async_step_link({'code': '123ABC'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'
    assert result['errors'] == {'code': 'invalid_code'}


async def test_verify_code_unknown_error(hass):
    """Test verify code unknown error."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    convert_code = Mock(side_effect=config_flow.NestAuthError)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, convert_code)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'

    result = await flow.async_step_link({'code': '123ABC'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'
    assert result['errors'] == {'code': 'unknown'}


async def test_verify_code_exception(hass):
    """Test verify code blows up."""
    gen_authorize_url = Mock(return_value=mock_coro('https://example.com'))
    convert_code = Mock(side_effect=ValueError)
    config_flow.register_flow_implementation(
        hass, 'test', 'Test', gen_authorize_url, convert_code)

    flow = config_flow.NestFlowHandler()
    flow.hass = hass
    result = await flow.async_step_init()
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'

    result = await flow.async_step_link({'code': '123ABC'})
    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'
    assert result['errors'] == {'code': 'internal_error'}


async def test_step_import(hass):
    """Test that we trigger import when configuring with client."""
    with patch('os.path.isfile', return_value=False):
        assert await async_setup_component(hass, DOMAIN, {
            DOMAIN: {
                'client_id': 'bla',
                'client_secret': 'bla',
            },
        })
        await hass.async_block_till_done()

    flow = hass.config_entries.flow.async_progress()[0]
    result = await hass.config_entries.flow.async_configure(flow['flow_id'])

    assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
    assert result['step_id'] == 'link'


async def test_step_import_with_token_cache(hass):
    """Test that we import existing token cache."""
    with patch('os.path.isfile', return_value=True), \
        patch('homeassistant.components.nest.config_flow.load_json',
              return_value={'access_token': 'yo'}), \
            patch('homeassistant.components.nest.async_setup_entry',
                  return_value=mock_coro(True)):
        assert await async_setup_component(hass, DOMAIN, {
            DOMAIN: {
                'client_id': 'bla',
                'client_secret': 'bla',
            },
        })
        await hass.async_block_till_done()

    entry = hass.config_entries.async_entries(DOMAIN)[0]

    assert entry.data == {
        'impl_domain': 'nest',
        'tokens': {
            'access_token': 'yo'
        }
    }