"""Test the onboarding views."""
import asyncio
from unittest.mock import patch

import pytest

from homeassistant.setup import async_setup_component
from homeassistant.components import onboarding
from homeassistant.components.onboarding import const, views

from tests.common import CLIENT_ID, register_auth_provider
from tests.components.met.conftest import mock_weather  # noqa

from . import mock_storage


@pytest.fixture(autouse=True)
def always_mock_weather(mock_weather):  # noqa
    """Mock the Met weather provider."""
    pass


@pytest.fixture(autouse=True)
def auth_active(hass):
    """Ensure auth is always active."""
    hass.loop.run_until_complete(register_auth_provider(hass, {
        'type': 'homeassistant'
    }))


async def test_onboarding_progress(hass, hass_storage, aiohttp_client):
    """Test fetching progress."""
    mock_storage(hass_storage, {
        'done': ['hello']
    })

    assert await async_setup_component(hass, 'onboarding', {})
    client = await aiohttp_client(hass.http.app)

    with patch.object(views, 'STEPS', ['hello', 'world']):
        resp = await client.get('/api/onboarding')

    assert resp.status == 200
    data = await resp.json()
    assert len(data) == 2
    assert data[0] == {
        'step': 'hello',
        'done': True
    }
    assert data[1] == {
        'step': 'world',
        'done': False
    }


async def test_onboarding_user_already_done(hass, hass_storage,
                                            aiohttp_client):
    """Test creating a new user when user step already done."""
    mock_storage(hass_storage, {
        'done': [views.STEP_USER]
    })

    with patch.object(onboarding, 'STEPS', ['hello', 'world']):
        assert await async_setup_component(hass, 'onboarding', {})

    client = await aiohttp_client(hass.http.app)

    resp = await client.post('/api/onboarding/users', json={
        'client_id': CLIENT_ID,
        'name': 'Test Name',
        'username': 'test-user',
        'password': 'test-pass',
        'language': 'en',
    })

    assert resp.status == 403


async def test_onboarding_user(hass, hass_storage, aiohttp_client):
    """Test creating a new user."""
    assert await async_setup_component(hass, 'person', {})
    assert await async_setup_component(hass, 'onboarding', {})

    client = await aiohttp_client(hass.http.app)

    resp = await client.post('/api/onboarding/users', json={
        'client_id': CLIENT_ID,
        'name': 'Test Name',
        'username': 'test-user',
        'password': 'test-pass',
        'language': 'en',
    })

    assert resp.status == 200
    assert const.STEP_USER in hass_storage[const.DOMAIN]['data']['done']

    data = await resp.json()
    assert 'auth_code' in data

    users = await hass.auth.async_get_users()
    assert len(users) == 1
    user = users[0]
    assert user.name == 'Test Name'
    assert len(user.credentials) == 1
    assert user.credentials[0].data['username'] == 'test-user'
    assert len(hass.data['person'].storage_data) == 1

    # Validate refresh token 1
    resp = await client.post('/auth/token', data={
        'client_id': CLIENT_ID,
        'grant_type': 'authorization_code',
        'code': data['auth_code']
    })

    assert resp.status == 200
    tokens = await resp.json()

    assert (
        await hass.auth.async_validate_access_token(tokens['access_token'])
        is not None
    )

    # Validate created areas
    area_registry = await hass.helpers.area_registry.async_get_registry()
    assert len(area_registry.areas) == 3
    assert sorted([area.name for area
                   in area_registry.async_list_areas()]) == [
        'Bedroom', 'Kitchen', 'Living Room'
    ]


async def test_onboarding_user_invalid_name(hass, hass_storage,
                                            aiohttp_client):
    """Test not providing name."""
    mock_storage(hass_storage, {
        'done': []
    })

    assert await async_setup_component(hass, 'onboarding', {})

    client = await aiohttp_client(hass.http.app)

    resp = await client.post('/api/onboarding/users', json={
        'client_id': CLIENT_ID,
        'username': 'test-user',
        'password': 'test-pass',
        'language': 'en',
    })

    assert resp.status == 400


async def test_onboarding_user_race(hass, hass_storage, aiohttp_client):
    """Test race condition on creating new user."""
    mock_storage(hass_storage, {
        'done': ['hello']
    })

    assert await async_setup_component(hass, 'onboarding', {})

    client = await aiohttp_client(hass.http.app)

    resp1 = client.post('/api/onboarding/users', json={
        'client_id': CLIENT_ID,
        'name': 'Test 1',
        'username': '1-user',
        'password': '1-pass',
        'language': 'en',
    })
    resp2 = client.post('/api/onboarding/users', json={
        'client_id': CLIENT_ID,
        'name': 'Test 2',
        'username': '2-user',
        'password': '2-pass',
        'language': 'es',
    })

    res1, res2 = await asyncio.gather(resp1, resp2)

    assert sorted([res1.status, res2.status]) == [200, 403]


async def test_onboarding_integration(hass, hass_storage, hass_client):
    """Test finishing integration step."""
    mock_storage(hass_storage, {
        'done': [const.STEP_USER]
    })

    assert await async_setup_component(hass, 'onboarding', {})

    client = await hass_client()

    resp = await client.post('/api/onboarding/integration', json={
        'client_id': CLIENT_ID,
    })

    assert resp.status == 200
    data = await resp.json()
    assert 'auth_code' in data

    # Validate refresh token
    resp = await client.post('/auth/token', data={
        'client_id': CLIENT_ID,
        'grant_type': 'authorization_code',
        'code': data['auth_code']
    })

    assert resp.status == 200
    assert const.STEP_INTEGRATION in hass_storage[const.DOMAIN]['data']['done']
    tokens = await resp.json()

    assert (
        await hass.auth.async_validate_access_token(tokens['access_token'])
        is not None
    )


async def test_onboarding_integration_requires_auth(hass, hass_storage,
                                                    aiohttp_client):
    """Test finishing integration step."""
    mock_storage(hass_storage, {
        'done': [const.STEP_USER]
    })

    assert await async_setup_component(hass, 'onboarding', {})

    client = await aiohttp_client(hass.http.app)

    resp = await client.post('/api/onboarding/integration', json={
        'client_id': CLIENT_ID,
    })

    assert resp.status == 401


async def test_onboarding_core_sets_up_met(hass, hass_storage, hass_client):
    """Test finishing the core step."""
    mock_storage(hass_storage, {
        'done': [const.STEP_USER]
    })

    assert await async_setup_component(hass, 'onboarding', {})

    client = await hass_client()

    resp = await client.post('/api/onboarding/core_config')

    assert resp.status == 200

    await hass.async_block_till_done()
    assert len(hass.states.async_entity_ids('weather')) == 1