 # -*- coding:utf-8 -*-

import json

import pytest
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse

from leaflet_storage.models import DataLayer, Map

from .base import login_required

pytestmark = pytest.mark.django_db
User = get_user_model()


@pytest.fixture
def post_data():
    return {
        'name': 'name',
        'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}',  # noqa
        'settings': '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}'  # noqa
    }


def test_create(client, user, post_data):
    url = reverse('map_create')
    # POST only mendatory fields
    name = 'test-map-with-new-name'
    post_data['name'] = name
    client.login(username=user.username, password="123123")
    response = client.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    created_map = Map.objects.latest('pk')
    assert j['id'] == created_map.pk
    assert created_map.name == name


def test_map_create_permissions(client):
    url = reverse('map_create')
    # POST anonymous
    response = client.post(url, {})
    assert login_required(response)


def test_map_update_access(client, map, user):
    url = reverse('map_update', kwargs={'map_id': map.pk})
    # GET anonymous
    response = client.get(url)
    assert login_required(response)
    # POST anonymous
    response = client.post(url, {})
    assert login_required(response)
    # GET with wrong permissions
    client.login(username=user.username, password="123123")
    response = client.get(url)
    assert response.status_code == 403
    # POST with wrong permissions
    client.login(username=user.username, password="123123")
    response = client.post(url, {})
    assert response.status_code == 403


def test_map_update_permissions_access(client, map, user):
    url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
    # GET anonymous
    response = client.get(url)
    assert login_required(response)
    # POST anonymous
    response = client.post(url, {})
    assert login_required(response)
    # GET with wrong permissions
    client.login(username=user.username, password="123123")
    response = client.get(url)
    assert response.status_code == 403
    # POST with wrong permissions
    client.login(username=user.username, password="123123")
    response = client.post(url, {})
    assert response.status_code == 403


def test_update(client, map, post_data):
    url = reverse('map_update', kwargs={'map_id': map.pk})
    # POST only mendatory fields
    name = 'new map name'
    post_data['name'] = name
    client.login(username=map.owner.username, password="123123")
    response = client.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    assert 'html' not in j
    updated_map = Map.objects.get(pk=map.pk)
    assert j['id'] == updated_map.pk
    assert updated_map.name == name


def test_delete(client, map, datalayer):
    url = reverse('map_delete', args=(map.pk, ))
    client.login(username=map.owner.username, password="123123")
    response = client.post(url, {}, follow=True)
    assert response.status_code == 200
    assert not Map.objects.filter(pk=map.pk).exists()
    assert not DataLayer.objects.filter(pk=datalayer.pk).exists()
    # Check that user has not been impacted
    assert User.objects.filter(pk=map.owner.pk).exists()
    # Test response is a json
    j = json.loads(response.content.decode())
    assert 'redirect' in j


def test_wrong_slug_should_redirect_to_canonical(client, map):
    url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
    canonical = reverse('map', kwargs={'pk': map.pk,
                                       'slug': map.slug})
    response = client.get(url)
    assert response.status_code == 301
    assert response['Location'] == canonical


def test_wrong_slug_should_redirect_with_query_string(client, map):
    url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
    url = "{}?allowEdit=0".format(url)
    canonical = reverse('map', kwargs={'pk': map.pk,
                                       'slug': map.slug})
    canonical = "{}?allowEdit=0".format(canonical)
    response = client.get(url)
    assert response.status_code == 301
    assert response['Location'] == canonical


def test_should_not_consider_the_query_string_for_canonical_check(client, map):
    url = reverse('map', kwargs={'pk': map.pk, 'slug': map.slug})
    url = "{}?allowEdit=0".format(url)
    response = client.get(url)
    assert response.status_code == 200


def test_short_url_should_redirect_to_canonical(client, map):
    url = reverse('map_short_url', kwargs={'pk': map.pk})
    canonical = reverse('map', kwargs={'pk': map.pk,
                                       'slug': map.slug})
    response = client.get(url)
    assert response.status_code == 301
    assert response['Location'] == canonical


def test_old_url_should_redirect_to_canonical(client, map):
    url = reverse(
        'map_old_url',
        kwargs={'username': map.owner.username, 'slug': map.slug}
    )
    canonical = reverse('map', kwargs={'pk': map.pk,
                                       'slug': map.slug})
    response = client.get(url)
    assert response.status_code == 301
    assert response['Location'] == canonical


def test_clone_map_should_create_a_new_instance(client, map):
    assert Map.objects.count() == 1
    url = reverse('map_clone', kwargs={'map_id': map.pk})
    client.login(username=map.owner.username, password="123123")
    response = client.post(url)
    assert response.status_code == 200
    assert Map.objects.count() == 2
    clone = Map.objects.latest('pk')
    assert clone.pk != map.pk
    assert clone.name == u"Clone of " + map.name


def test_user_not_allowed_should_not_clone_map(client, map, user):
    assert Map.objects.count() == 1
    url = reverse('map_clone', kwargs={'map_id': map.pk})
    map.edit_status = map.OWNER
    map.save()
    response = client.post(url)
    assert login_required(response)
    client.login(username=user.username, password="123123")
    response = client.post(url)
    assert response.status_code == 403
    map.edit_status = map.ANONYMOUS
    map.save()
    client.logout()
    response = client.post(url)
    assert response.status_code == 403
    assert Map.objects.count() == 1


def test_clone_should_set_cloner_as_owner(client, map, user):
    url = reverse('map_clone', kwargs={'map_id': map.pk})
    map.edit_status = map.EDITORS
    map.editors.add(user)
    map.save()
    client.login(username=user.username, password="123123")
    response = client.post(url)
    assert response.status_code == 200
    assert Map.objects.count() == 2
    clone = Map.objects.latest('pk')
    assert clone.pk != map.pk
    assert clone.name == u"Clone of " + map.name
    assert clone.owner == user


def test_map_creation_should_allow_unicode_names(client, map, post_data):
    url = reverse('map_create')
    # POST only mendatory fields
    name = u'Академический'
    post_data['name'] = name
    client.login(username=map.owner.username, password="123123")
    response = client.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    created_map = Map.objects.latest('pk')
    assert j['id'] == created_map.pk
    assert created_map.name == name
    # Lower case of the russian original name
    # self.assertEqual(created_map.slug, u"академический")
    # for now we fallback to "map", see unicode_name branch
    assert created_map.slug == 'map'


def test_anonymous_can_access_map_with_share_status_public(client, map):
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.PUBLIC
    map.save()
    response = client.get(url)
    assert response.status_code == 200


def test_anonymous_can_access_map_with_share_status_open(client, map):
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.OPEN
    map.save()
    response = client.get(url)
    assert response.status_code == 200


def test_anonymous_cannot_access_map_with_share_status_private(client, map):
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.PRIVATE
    map.save()
    response = client.get(url)
    assert response.status_code == 403


def test_owner_can_access_map_with_share_status_private(client, map):
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.PRIVATE
    map.save()
    client.login(username=map.owner.username, password="123123")
    response = client.get(url)
    assert response.status_code == 200


def test_editors_can_access_map_with_share_status_private(client, map, user):
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.PRIVATE
    map.editors.add(user)
    map.save()
    client.login(username=user.username, password="123123")
    response = client.get(url)
    assert response.status_code == 200


def test_non_editor_cannot_access_map_if_share_status_private(client, map, user):  # noqa
    url = reverse('map', args=(map.slug, map.pk))
    map.share_status = map.PRIVATE
    map.save()
    client.login(username=user.username, password="123123")
    response = client.get(url)
    assert response.status_code == 403


def test_map_geojson_view(client, map):
    url = reverse('map_geojson', args=(map.pk, ))
    response = client.get(url)
    j = json.loads(response.content.decode())
    assert 'type' in j


def test_only_owner_can_delete(client, map, user):
    map.editors.add(user)
    url = reverse('map_delete', kwargs={'map_id': map.pk})
    client.login(username=user.username, password="123123")
    response = client.post(url, {}, follow=True)
    assert response.status_code == 403


def test_map_editors_do_not_see_owner_change_input(client, map, user):
    map.editors.add(user)
    map.edit_status = map.EDITORS
    map.save()
    url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
    client.login(username=user.username, password="123123")
    response = client.get(url)
    assert 'id_owner' not in response


def test_logged_in_user_can_edit_map_editable_by_anonymous(client, map, user):
    map.owner = None
    map.edit_status = map.ANONYMOUS
    map.save()
    client.login(username=user.username, password="123123")
    url = reverse('map_update', kwargs={'map_id': map.pk})
    new_name = 'this is my new name'
    data = {
        'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}',  # noqa
        'name': new_name
    }
    response = client.post(url, data)
    assert response.status_code == 200
    assert Map.objects.get(pk=map.pk).name == new_name


@pytest.mark.usefixtures('allow_anonymous')
def test_anonymous_create(cookieclient, post_data):
    url = reverse('map_create')
    # POST only mendatory fields
    name = 'test-map-with-new-name'
    post_data['name'] = name
    response = cookieclient.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    created_map = Map.objects.latest('pk')
    assert j['id'] == created_map.pk
    assert created_map.name == name
    key, value = created_map.signed_cookie_elements
    assert key in cookieclient.cookies


@pytest.mark.usefixtures('allow_anonymous')
def test_anonymous_update_without_cookie_fails(client, anonymap, post_data):  # noqa
    url = reverse('map_update', kwargs={'map_id': anonymap.pk})
    response = client.post(url, post_data)
    assert response.status_code == 403


@pytest.mark.usefixtures('allow_anonymous')
def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data):  # noqa
    url = reverse('map_update', kwargs={'map_id': anonymap.pk})
    # POST only mendatory fields
    name = 'new map name'
    post_data['name'] = name
    response = cookieclient.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    updated_map = Map.objects.get(pk=anonymap.pk)
    assert j['id'] == updated_map.pk


@pytest.mark.usefixtures('allow_anonymous')
def test_anonymous_delete(cookieclient, anonymap):
    url = reverse('map_delete', args=(anonymap.pk, ))
    response = cookieclient.post(url, {}, follow=True)
    assert response.status_code == 200
    assert not Map.objects.filter(pk=anonymap.pk).count()
    # Test response is a json
    j = json.loads(response.content.decode())
    assert 'redirect' in j


@pytest.mark.usefixtures('allow_anonymous')
def test_no_cookie_cant_delete(client, anonymap):
    url = reverse('map_delete', args=(anonymap.pk, ))
    response = client.post(url, {}, follow=True)
    assert response.status_code == 403


@pytest.mark.usefixtures('allow_anonymous')
def test_anonymous_edit_url(cookieclient, anonymap):
    url = anonymap.get_anonymous_edit_url()
    canonical = reverse('map', kwargs={'pk': anonymap.pk,
                                       'slug': anonymap.slug})
    response = cookieclient.get(url)
    assert response.status_code == 302
    assert response['Location'] == canonical
    key, value = anonymap.signed_cookie_elements
    assert key in cookieclient.cookies


@pytest.mark.usefixtures('allow_anonymous')
def test_bad_anonymous_edit_url_should_return_403(cookieclient, anonymap):
    url = anonymap.get_anonymous_edit_url()
    url = reverse(
        'map_anonymous_edit_url',
        kwargs={'signature': "%s:badsignature" % anonymap.pk}
    )
    response = cookieclient.get(url)
    assert response.status_code == 403


@pytest.mark.usefixtures('allow_anonymous')
def test_authenticated_user_with_cookie_is_attached_as_owner(cookieclient, anonymap, post_data, user):  # noqa
    url = reverse('map_update', kwargs={'map_id': anonymap.pk})
    cookieclient.login(username=user.username, password="123123")
    assert anonymap.owner is None
    # POST only mendatory filds
    name = 'new map name for authenticat_anonymoused user'
    post_data['name'] = name
    response = cookieclient.post(url, post_data)
    assert response.status_code == 200
    j = json.loads(response.content.decode())
    updated_map = Map.objects.get(pk=anonymap.pk)
    assert j['id'] == updated_map.pk
    assert updated_map.owner.pk, user.pk


@pytest.mark.usefixtures('allow_anonymous')
def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(client, anonymap, user):  # noqa
    assert Map.objects.count() == 1
    url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
    anonymap.edit_status = anonymap.OWNER
    anonymap.save()
    response = client.post(url)
    assert response.status_code == 403
    client.login(username=user.username, password="123123")
    response = client.post(url)
    assert response.status_code == 403
    assert Map.objects.count() == 1


@pytest.mark.usefixtures('allow_anonymous')
def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap):  # noqa
    assert Map.objects.count() == 1
    url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
    anonymap.edit_status = anonymap.ANONYMOUS
    anonymap.save()
    response = client.post(url)
    assert response.status_code == 200
    assert Map.objects.count() == 2
    clone = Map.objects.latest('pk')
    assert clone.pk != anonymap.pk
    assert clone.name == 'Clone of ' + anonymap.name
    assert clone.owner is None


@pytest.mark.usefixtures('allow_anonymous')
def test_anyone_can_access_anonymous_map(cookieclient, anonymap):
    url = reverse('map', args=(anonymap.slug, anonymap.pk))
    anonymap.share_status = anonymap.PUBLIC
    response = cookieclient.get(url)
    assert response.status_code == 200
    anonymap.share_status = anonymap.OPEN
    response = cookieclient.get(url)
    assert response.status_code == 200
    anonymap.share_status = anonymap.PRIVATE
    response = cookieclient.get(url)
    assert response.status_code == 200
