import pytest
from django.contrib.contenttypes.models import ContentType
from django.core.serializers.json import DjangoJSONEncoder

from passerelle.apps.jsondatastore.models import JsonDataStore
from passerelle.base.models import AccessRight, ApiUser


@pytest.fixture
def jsondatastore(db):
    datastore = JsonDataStore.objects.create(slug='foobar')
    api = ApiUser.objects.create(username='all', keytype='', key='')
    obj_type = ContentType.objects.get_for_model(datastore)
    AccessRight.objects.create(
        codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=datastore.pk
    )
    return datastore


@pytest.fixture
def jsondatastore2(db):
    datastore = JsonDataStore.objects.create(slug='foobar2')
    api = ApiUser.objects.create(username='all2', keytype='', key='')
    obj_type = ContentType.objects.get_for_model(datastore)
    AccessRight.objects.create(
        codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=datastore.pk
    )
    return datastore


def test_jsondatastore(app, jsondatastore, jsondatastore2):
    resp = app.get('/jsondatastore/foobar/data/')
    assert resp.json == {'data': [], 'err': 0}

    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    uuid = resp.json['id']
    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 1
    assert resp.json['data'][0]['content'] == {'foo': 'bar'}

    # check json payload
    resp = app.post('/jsondatastore/foobar/data/create', params='foo=bar')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'could not decode body to json'
    resp = app.post_json('/jsondatastore/foobar/data/create', params='foo=bar')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'payload must be a dict'

    resp = app.get('/jsondatastore/foobar/data/%s/' % uuid)
    assert resp.json['id'] == uuid
    assert resp.json['content'] == {'foo': 'bar'}

    resp = app.post_json('/jsondatastore/foobar/data/%s/' % uuid, params={'foo': 'bar2'})
    assert resp.json['id'] == uuid
    assert resp.json['content'] == {'foo': 'bar2'}

    # check json payload
    resp = app.post('/jsondatastore/foobar/data/%s/' % uuid, params='foo=bar2')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'could not decode body to json'
    resp = app.post_json('/jsondatastore/foobar/data/%s/' % uuid, params='foo=bar2')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'payload must be a dict'

    resp = app.get('/jsondatastore/foobar/data/%s/' % uuid)
    assert resp.json['id'] == uuid
    assert resp.json['content'] == {'foo': 'bar2'}

    resp = app.patch_json('/jsondatastore/foobar/data/%s/' % uuid, params={'foo2': 'bar2'})
    assert resp.json['id'] == uuid
    assert resp.json['content'] == {'foo': 'bar2', 'foo2': 'bar2'}

    # check json payload
    resp = app.patch('/jsondatastore/foobar/data/%s/' % uuid, params='foo2=bar2')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'could not decode body to json'
    resp = app.patch_json('/jsondatastore/foobar/data/%s/' % uuid, params='foo2=bar2')
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == 'payload must be a dict'

    resp = app.post_json('/jsondatastore/foobar/data/%s/delete' % uuid)
    assert resp.json['err'] == 0

    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 0

    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    uuid = resp.json['id']
    resp = app.get('/jsondatastore/foobar2/data/')
    assert len(resp.json['data']) == 0

    resp = app.post_json('/jsondatastore/foobar2/data/%s/delete' % uuid, status=404)
    assert resp.json['err'] == 1


def test_jsondatastore_name_id(app, jsondatastore):
    resp = app.get('/jsondatastore/foobar/data/')
    assert resp.json == {'data': [], 'err': 0}

    resp = app.post_json('/jsondatastore/foobar/data/create?name_id=xxx', params={'foo': 'bar'})
    uuid = resp.json['id']
    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 1
    assert resp.json['data'][0]['content'] == {'foo': 'bar'}

    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 1
    resp = app.get('/jsondatastore/foobar/data/?name_id=xxx')
    assert len(resp.json['data']) == 1
    resp = app.get('/jsondatastore/foobar/data/?name_id=yyy')
    assert len(resp.json['data']) == 0
    resp = app.get('/jsondatastore/foobar/data/?name_id=')
    assert len(resp.json['data']) == 0

    resp = app.post_json('/jsondatastore/foobar/data/create?name_id=yyy', params={'foo': 'bar'})
    uuid = resp.json['id']
    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 2
    resp = app.get('/jsondatastore/foobar/data/?name_id=yyy')
    assert len(resp.json['data']) == 1

    # try removing an existing entry with the wrong name id
    resp = app.post_json('/jsondatastore/foobar/data/%s/delete?name_id=zzz' % uuid, status=404)
    assert resp.json['err'] == 1

    # try removing an existing entry with a blank name id
    resp = app.post_json('/jsondatastore/foobar/data/%s/delete?name_id=' % uuid, status=404)
    assert resp.json['err'] == 1

    # and with the right one
    resp = app.post_json('/jsondatastore/foobar/data/%s/delete?name_id=yyy' % uuid)
    assert resp.json['err'] == 0


def test_jsondatastore_template(app, jsondatastore):
    jsondatastore.text_value_template = '{{foo}}'
    jsondatastore.save()
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 1
    assert resp.json['data'][0]['text'] == 'bar'

    # check entries are alphabetically sorted
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'aaa'})
    resp = app.get('/jsondatastore/foobar/data/')
    assert len(resp.json['data']) == 2
    assert resp.json['data'][0]['text'] == 'aaa'
    assert resp.json['data'][1]['text'] == 'bar'


def test_jsondatastore_get_by_attribute(app, jsondatastore):
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    uuid = resp.json['id']
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar2'})
    uuid2 = resp.json['id']

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar'})
    assert resp.json['id'] == uuid

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar2'})
    assert resp.json['id'] == uuid2

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar3'})
    assert resp.json['err'] == 1

    resp = app.post_json('/jsondatastore/foobar/data/create?name_id=xxx', params={'foo': 'bar3'})
    uuid = resp.json['id']
    resp = app.post_json('/jsondatastore/foobar/data/create?name_id=yyy', params={'foo': 'bar3'})
    uuid2 = resp.json['id']

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar3', 'name_id': 'xxx'})
    assert resp.json['id'] == uuid

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar3', 'name_id': 'yyy'})
    assert resp.json['id'] == uuid2

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar3', 'name_id': 'zzz'})
    assert resp.json['err'] == 1


def test_jsondatastore_datetimes(app, jsondatastore):
    encoder = DjangoJSONEncoder()
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    uuid = resp.json['id']
    creation_datetime = jsondatastore.get_data_object(uuid).creation_datetime

    resp = app.post_json('/jsondatastore/foobar/data/%s/' % uuid, params={'foo': 'bar2'})
    last_update_datetime = jsondatastore.get_data_object(uuid).last_update_datetime
    assert resp.json['creation_datetime'] == encoder.default(creation_datetime)
    assert resp.json['last_update_datetime'] == encoder.default(last_update_datetime)

    resp = app.get('/jsondatastore/foobar/data/by/foo/', params={'value': 'bar2'})
    assert resp.json['creation_datetime'] == encoder.default(creation_datetime)
    assert resp.json['last_update_datetime'] == encoder.default(last_update_datetime)

    resp = app.get('/jsondatastore/foobar/data/')
    assert resp.json['data'][0]['creation_datetime'] == encoder.default(creation_datetime)
    assert resp.json['data'][0]['last_update_datetime'] == encoder.default(last_update_datetime)


def test_jsondatastore_list_by_attribute_filter(app, jsondatastore):
    resp = app.post_json(
        '/jsondatastore/foobar/data/create?name_id=name1', params={'key1': 'val1', 'key2': 'foo'}
    )
    uuid1 = resp.json['id']
    resp = app.post_json(
        '/jsondatastore/foobar/data/create?name_id=name2', params={'key1': 'val1', 'key2': 'val2'}
    )
    uuid2 = resp.json['id']
    resp = app.post_json(
        '/jsondatastore/foobar/data/create?name_id=name1', params={'key1': 'val1', 'key2': 'val2'}
    )
    uuid3 = resp.json['id']

    resp = app.get('/jsondatastore/foobar/data/', params={'key2': 'foo'})
    assert [d['id'] for d in resp.json['data']] == [uuid1]

    resp = app.get('/jsondatastore/foobar/data/', params={'key1': 'val1', 'key2': 'val2'})
    assert sorted(d['id'] for d in resp.json['data']) == sorted([uuid2, uuid3])

    resp = app.get('/jsondatastore/foobar/data/?name_id=name1', params={'key2': 'val2'})
    assert [d['id'] for d in resp.json['data']] == [uuid3]

    resp = app.get('/jsondatastore/foobar/data/', params={'key3': ''})
    assert [d['id'] for d in resp.json['data']] == []


def test_jsondatastore_list_by_q_attribute(app, jsondatastore):
    jsondatastore.text_value_template = '{{foo}}'
    jsondatastore.save()
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'bar'})
    uuid1 = resp.json['id']
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'BÄR'})
    uuid2 = resp.json['id']
    resp = app.post_json('/jsondatastore/foobar/data/create', params={'foo': 'ras'})

    resp = app.get('/jsondatastore/foobar/data/', params={'q': 'àR'})
    assert sorted(d['id'] for d in resp.json['data']) == sorted([uuid1, uuid2])

    resp = app.get('/jsondatastore/foobar/data/', params={'q': 'na'})
    assert resp.json['data'] == []
