import mock
import pytest
import json

import six

from awx.api.versioning import reverse
from awx.main.utils import timestamp_apiformat
from django.utils import timezone


def mock_feature_enabled(feature):
    return True


def mock_feature_disabled(feature):
    return False


# TODO: Consider making the fact_scan() fixture a Class, instead of a function, and move this method into it
def find_fact(facts, host_id, module_name, timestamp):
    for f in facts:
        if f.host_id == host_id and f.module == module_name and f.timestamp == timestamp:
            return f
    raise RuntimeError('fact <%s, %s, %s> not found in %s', (host_id, module_name, timestamp, facts))


def setup_common(hosts, fact_scans, get, user, epoch=timezone.now(), module_name='ansible', get_params={}):
    hosts = hosts(host_count=1)
    facts = fact_scans(fact_scans=1, timestamp_epoch=epoch)

    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = get(url, user('admin', True), data=get_params)

    fact_known = find_fact(facts, hosts[0].id, module_name, epoch)
    return (fact_known, response)


def check_system_tracking_feature_forbidden(response):
    assert 402 == response.status_code
    assert 'Your license does not permit use of system tracking.' == response.data['detail']


@mock.patch('awx.api.views.mixin.feature_enabled', new=mock_feature_disabled)
@pytest.mark.django_db
@pytest.mark.license_feature
def test_system_tracking_license_get(hosts, get, user):
    hosts = hosts(host_count=1)
    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = get(url, user('admin', True))

    check_system_tracking_feature_forbidden(response)


@mock.patch('awx.api.views.mixin.feature_enabled', new=mock_feature_disabled)
@pytest.mark.django_db
@pytest.mark.license_feature
def test_system_tracking_license_options(hosts, options, user):
    hosts = hosts(host_count=1)
    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = options(url, None, user('admin', True))

    check_system_tracking_feature_forbidden(response)


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_no_fact_found(hosts, get, user):
    hosts = hosts(host_count=1)
    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = get(url, user('admin', True))

    expected_response = {
        "detail": "Fact not found."
    }
    assert 404 == response.status_code
    assert expected_response == response.data


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_basic_fields(hosts, fact_scans, get, user, monkeypatch_jsonbfield_get_db_prep_save):
    hosts = hosts(host_count=1)
    fact_scans(fact_scans=1)

    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = get(url, user('admin', True))

    assert 'related' in response.data
    assert 'id' in response.data
    assert 'facts' in response.data
    assert 'module' in response.data
    assert 'host' in response.data
    assert isinstance(response.data['host'], int)
    assert 'summary_fields' in response.data
    assert 'host' in response.data['summary_fields']
    assert 'name' in response.data['summary_fields']['host']
    assert 'description' in response.data['summary_fields']['host']
    assert 'host' in response.data['related']
    assert reverse('api:host_detail', kwargs={'pk': hosts[0].pk}) == response.data['related']['host']


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_content(hosts, fact_scans, get, user, fact_ansible_json, monkeypatch_jsonbfield_get_db_prep_save):
    (fact_known, response) = setup_common(hosts, fact_scans, get, user)

    assert fact_known.host_id == response.data['host']
    # TODO: Just make response.data['facts'] when we're only dealing with postgres, or if jsonfields ever fixes this bug
    assert fact_ansible_json == (json.loads(response.data['facts']) if isinstance(response.data['facts'], six.text_type) else response.data['facts'])
    assert timestamp_apiformat(fact_known.timestamp) == response.data['timestamp']
    assert fact_known.module == response.data['module']


def _test_search_by_module(hosts, fact_scans, get, user, fact_json, module_name):
    params = {
        'module': module_name
    }
    (fact_known, response) = setup_common(hosts, fact_scans, get, user, module_name=module_name, get_params=params)

    # TODO: Just make response.data['facts'] when we're only dealing with postgres, or if jsonfields ever fixes this bug
    assert fact_json == (json.loads(response.data['facts']) if isinstance(response.data['facts'], six.text_type) else response.data['facts'])
    assert timestamp_apiformat(fact_known.timestamp) == response.data['timestamp']
    assert module_name == response.data['module']


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_search_by_module_packages(hosts, fact_scans, get, user, fact_packages_json, monkeypatch_jsonbfield_get_db_prep_save):
    _test_search_by_module(hosts, fact_scans, get, user, fact_packages_json, 'packages')


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_search_by_module_services(hosts, fact_scans, get, user, fact_services_json, monkeypatch_jsonbfield_get_db_prep_save):
    _test_search_by_module(hosts, fact_scans, get, user, fact_services_json, 'services')


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.django_db
def test_search_by_timestamp_and_module(hosts, fact_scans, get, user, fact_packages_json, monkeypatch_jsonbfield_get_db_prep_save):
    epoch = timezone.now()
    module_name = 'packages'

    (fact_known, response) = setup_common(
        hosts, fact_scans, get, user, module_name=module_name, epoch=epoch,
        get_params=dict(module=module_name, datetime=epoch)
    )

    assert fact_known.id == response.data['id']


def _test_user_access_control(hosts, fact_scans, get, user_obj, team_obj):
    hosts = hosts(host_count=1)
    fact_scans(fact_scans=1)

    team_obj.member_role.members.add(user_obj)

    url = reverse('api:host_fact_compare_view', kwargs={'pk': hosts[0].pk})
    response = get(url, user_obj)
    return response


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.ac
@pytest.mark.django_db
def test_normal_user_403(hosts, fact_scans, get, user, team, monkeypatch_jsonbfield_get_db_prep_save):
    user_bob = user('bob', False)
    response = _test_user_access_control(hosts, fact_scans, get, user_bob, team)

    assert 403 == response.status_code
    assert "You do not have permission to perform this action."  == response.data['detail']


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.ac
@pytest.mark.django_db
def test_super_user_ok(hosts, fact_scans, get, user, team, monkeypatch_jsonbfield_get_db_prep_save):
    user_super = user('bob', True)
    response = _test_user_access_control(hosts, fact_scans, get, user_super, team)

    assert 200 == response.status_code


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.ac
@pytest.mark.django_db
def test_user_admin_ok(organization, hosts, fact_scans, get, user, team, monkeypatch_jsonbfield_get_db_prep_save):
    user_admin = user('johnson', False)
    organization.admin_role.members.add(user_admin)

    response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)

    assert 200 == response.status_code


@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
@pytest.mark.ac
@pytest.mark.django_db
def test_user_admin_403(organization, organizations, hosts, fact_scans, get, user, team, monkeypatch_jsonbfield_get_db_prep_save):
    user_admin = user('johnson', False)
    org2 = organizations(1)
    org2[0].admin_role.members.add(user_admin)

    response = _test_user_access_control(hosts, fact_scans, get, user_admin, team)

    assert 403 == response.status_code
