"""
Functional tests for the managing sites.

:copyright: Copyright 2013-2017, Dorian Puła
    <dorian.pula@amber-penguin-software.ca>
:license: AGPL v3+
"""

import copy
import http
from unittest import mock

import pytest
import requests

from rookeries.sites import models
from tests import utils


# TODO: Rework site management and testing.
# TODO: Rework modification to only allow changes to layout and children...
# TODO: Add a test if the site does not exist for some reason.


@pytest.fixture(scope='module')
def valid_user(db):
    user, password = utils.generate_test_user(db)
    return user.username, password


@pytest.fixture
def auth_token_headers(valid_user, api_base_uri):
    username, password = valid_user
    token = requests.post(
        url=f'{api_base_uri}/auth',
        json={
            'username': username,
            'password': password,
        }
    ).json()['access_token']

    return {'Authorization': f'JWT {token}'}


def test_visitor_can_access_site(api_base_uri, test_site):
    response = requests.get(
        url=f'{api_base_uri}/api/site',
        headers={},
    )
    assert_site_response(response)


def test_authenticated_user_can_access_site(
        auth_token_headers, api_base_uri, test_site):
    response = requests.get(
        url=f'{api_base_uri}/api/site',
        headers=auth_token_headers,
    )
    assert_site_response(response)


def test_authenticated_user_cannot_modify_site_using_bad_request(
        auth_token_headers, api_base_uri, test_site):
    response = requests.put(
        url=f'{api_base_uri}/api/site',
        data='',
        headers=auth_token_headers,
    )
    assert_bad_request_response(response)


def test_authenticated_user_cannot_modify_site_using_empty_request(
        auth_token_headers, api_base_uri, test_site):
    response = requests.put(
        url=f'{api_base_uri}/api/site',
        json={},
        headers=auth_token_headers,
    )
    assert_bad_request_response(response)


def test_authenticated_user_can_modify_site_using_proper_request(
        auth_token_headers, api_base_uri, test_site, db):

    original_site = get_site_request(auth_token_headers, api_base_uri)
    modify_site = copy.deepcopy(original_site)
    modify_site['name'] = 'Modified Test Site'

    response = requests.put(
        url=f'{api_base_uri}/api/site',
        json=modify_site,
        headers=auth_token_headers,
    )
    assert_updated_site_response(test_site, response, db)


def get_site_request(auth_token_headers, api_base_uri):
    return requests.get(
        url=f'{api_base_uri}/api/site',
        headers=auth_token_headers,
    ).json()


def assert_site_response(response):
    assert response.status_code == http.HTTPStatus.OK

    expected_json = {
        'name': 'Test Site',
        'links': {'self': '/api/site'},
        'config': mock.ANY,
        'landingPage': mock.ANY,
        'menu': mock.ANY,
        'header': '',
        'footer': '',
    }
    assert expected_json == response.json()


def assert_unauthorized_response(expected_url, response):
    expected_response_json = {
        'error': {
            'status_code': http.HTTPStatus.UNAUTHORIZED.value,
            'message': 'Not authorized to access this resource.',
            'resource': expected_url,
        }
    }

    assert response.status_code == http.HTTPStatus.UNAUTHORIZED
    assert response.json() == expected_response_json


def assert_bad_request_response(response):
    expected_response_json = {
        'error': {
            'status_code': http.HTTPStatus.BAD_REQUEST.value,
            'message': mock.ANY,
        }
    }

    assert response.json() == expected_response_json
    assert response.status_code == http.HTTPStatus.BAD_REQUEST


def assert_updated_site_response(test_site, api_response, db):
    actual_response = utils.extract_response(api_response)
    assert actual_response.status_code == http.HTTPStatus.OK

    expected_json = test_site.to_api_json()
    expected_json['name'] = 'Modified Test Site'
    assert expected_json == actual_response.json()
    assert '/api/site' == actual_response.json()['links']['self']

    # TODO: Remember to reset the state of the site to its original here.
    updated_site = models.Site.find_in_db(db, {})
    updated_site.name = test_site.name
    updated_site.config = test_site.config
    updated_site.menu = test_site.menu
    updated_site.landing_page = test_site.landing_page
    db.create_or_update_document(
        updated_site.to_db_json(), updated_site._id, updated_site._rev)
