#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
from os import path

from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils import timezone
from django.conf import settings
from django.utils.translation import ugettext_lazy as _


from lostplaces.models import Place
from lostplaces.views import (
    PlaceCreateView,
    PlaceListView,
    PlaceDetailView
)
from lostplaces.forms import PlaceImageForm, PlaceForm
from lostplaces.tests.views import (
    ViewTestCase,
    TaggableViewTestCaseMixin,
    MapableViewTestCaseMixin,
    GlobalTemplateTestCaseMixin
)


class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
    """
    Tests the view listing all placs
    """
    view = PlaceListView

    @classmethod
    def setUpTestData(cls):
        user = User.objects.create_user(
            username='testpeter',
            password='Develop123',
        )
        user.explorer.level = 3
        user.explorer.save()

        # default level should be 1, not setting required
        other_user = User.objects.create_user(
            username='blubberbernd',
            password='Develop123'
        )

        superuser = User.objects.create_user(
            username='toor',
            password='Develop123'
        )

        superuser.is_superuser = True
        superuser.save()

        Place.objects.create(
            name='Im a own place',
            submitted_when=timezone.now(),
            submitted_by=other_user.explorer,
            location='Test %d town' % 5,
            latitude=50.5 + 5/10,
            longitude=7.0 - 5/10,
            description='This is just a test, do not worry %d' % 5,
            level=3
        )

        for i in range(12):
            place = Place.objects.create(
                name='Im a place %d' % i,
                submitted_when=timezone.now(),
                submitted_by=user.explorer,
                location='Test %d town' % i,
                latitude=50.5 + i/10,
                longitude=7.0 - i/10,
                description='This is just a test, do not worry %d' % i,
                level=3
            )
        place.tags.add('I a tag', 'testlocation')
        place.save()

    def setUp(self):
        self.client = Client()

    def test_list_view(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertContext(response, 'mapping_config')
        self.assertGlobal(response)
        
    def test_first_places(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_list'))
        
        for i in range(1, 4):
            place = Place.objects.get(id=i)
            self.assertTrue(
                place.name in response.content.decode(),
                msg='Expecting the %dth place name to be on the first page of list' % i
            )
            self.assertTrue(
                place.description in response.content.decode(),
                msg='Expecting the %dth place description to be on the first page of list' % i
            )
        
    def test_pagination(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertNotEqual(
            None,
            re.search(
                """<ul.*pagination.*>\s*(<li.*>.*</li>\s*){6,}</ul>""",
                response.content.decode().replace('\n', '').lower()
            ),
            msg='Expecting the place list to be paginated like [first] [previous] [item] at least 2 times [next] [last]'
        )

    def test_not_eligible_to_see_because_of_low_level(self):
        self.client.login(username='blubberbernd', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertFalse(
            'Im a place' in response.content.decode(),
            msg='Expecting the user to not see any places'
        )

    def test_not_eligible_to_see_because_of_low_level_superuser(self):
        self.client.login(username='toor', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertTrue(
            'Im a place' in response.content.decode(),
            msg='Expecting the superuser to see all places'
        )

    def test_not_eligible_to_see_because_of_low_level_own_place(self):
        self.client.login(username='blubberbernd', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertTrue(
            'Im a own place' in response.content.decode(),
            msg='Expecting the user to see it\'s own places'
        )

    def test_eligible_to_see(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_list'))

        self.assertTrue(
            'Im a own place' in response.content.decode(),
            msg='Expecting the user to see places where their level is high enough'
        )
        
class TestPlaceCreateView(ViewTestCase):
    view = PlaceCreateView

    @classmethod
    def setUpTestData(cls):
        user = User.objects.create_user(
            username='testpeter',
            password='Develop123'
        )

    def setUp(self):
        self.client = Client()
        
    def test_has_forms(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_create'))

        self.assertHasForm(response, 'place_image_form', PlaceImageForm)
        self.assertHasForm(response, 'place_form', PlaceForm)
        
    def test_positive_no_image(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.post(
            reverse('place_create'),
            data={
                'name': 'test place 486',
                'location': 'test location',
                'latitude': 45.804192,
                'longitude': 1.860222,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """
            },
            follow=True
        )
        self.assertHttpOK(response)
        place =  Place.objects.get(name='test place 486')
        self.assertNotEqual(
            None,
            place,
            msg='Submitted place not found in database / model'
        ),
        self.assertEqual(
            reverse(
                'place_detail', kwargs={'pk': place.id}
            ),
            response.redirect_chain[-1][0]
        )
        self.assertMessage(
            response,
            _('Successfully created place'),
            'success',
            msg='Expecting a visible success message'
        )
        
    def test_positive_image(self):
        self.client.login(username='testpeter', password='Develop123')
        image = open(
            path.join(
                settings.BASE_DIR,
                'testdata',
                'test_image.jpg'
            ),
            'rb'
        )
        response = self.client.post(
            reverse('place_create'),
            data={
                'name': 'test place 894',
                'location': 'test location',
                'latitude': 45.804192,
                'longitude': 1.860222,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """,
                'filename': [image]
            },
            follow=True
        )
        self.assertHttpOK(response)
        place =  Place.objects.get(name='test place 894')
        self.assertNotEqual(
            None,
            place,
            msg='Submitted place not found in database / model'
        ),
        self.assertEqual(
            reverse(
                'place_detail', kwargs={'pk': place.id}
            ),
            response.redirect_chain[-1][0]
        )
        self.assertEqual(
            len(place.placeimages.all()),
            1,
            msg='Expecting the place to have exactly 1 place image'
        )
        self.assertMessage(
            response,
            _('Successfully created place'),
            'success',
            msg='Expecting a visible success message'
        )
        
    def test_negative_no_name(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.post(
                reverse('place_create'),
            {
                'location': 'test location 456',
                'latitude': 45.804192,
                'longitude': 1.860222,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """
            }
        )
        self.assertHttpBadRequest(response)
        self.assertEqual(
            len(Place.objects.filter(location='test location 456')),
            0,
            msg='Expecting no place to be created'
        )
        self.assertMessage(
            response,
            _('Please fill in all required fields.'),
            'error',
            msg='Expecing a visible error message'
        )
        
    def test_negative_no_location(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.post(
                reverse('place_create'),
            {
                'name': 'test name 376',
                'latitude': 45.804192,
                'longitude': 1.860222,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """
            }
        )
        self.assertHttpBadRequest(response)
        self.assertEqual(
            len(Place.objects.filter(name='test name 376')),
            0,
            msg='Expecting no place to be created'
        )
        self.assertMessage(
            response,
            _('Please fill in all required fields.'),
            'error',
            msg='Expecing a visible error message'
        )
        
    def test_negative_no_latitude(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.post(
                reverse('place_create'),
            {
                'name': 'test name 417',
                'location': 'location',
                'longitude': 1.860222,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """
            }
        )
        self.assertHttpBadRequest(response)
        self.assertEqual(
            len(Place.objects.filter(name='test name 417')),
            0,
            msg='Expecting no place to be created'
        )
        self.assertMessage(
            response,
            _('Please fill in all required fields.'),
            'error',
            msg='Expecing a visible error message'
        )
        
    def test_negative_no_longitude(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.post(
            reverse('place_create'),
            {
                'name': 'test name 417',
                'location': 'location',
                'latitude': 45.804192,
                'description': """
                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
                """
            }
        )
        self.assertHttpBadRequest(response)
        self.assertEqual(
            len(Place.objects.filter(name='test name 417')),
            0,
            msg='Expecting no place to be created'
        )
        self.assertMessage(
            response,
            _('Please fill in all required fields.'),
            'error',
            msg='Expecing a visible error message'
        )

class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixin, ViewTestCase):
    view = PlaceDetailView

    @classmethod
    def setUpTestData(cls):
        user = User.objects.create_user(
            username='testpeter',
            password='Develop123'
        )
        user.explorer.level = 3
        user.explorer.save()

        place = Place.objects.create(
            name='Im a place',
            submitted_when=timezone.now(),
            submitted_by=user.explorer,
            location='Testtown',
            latitude=50.5,
            longitude=7.0,
            description='This is just a test, do not worry',
            level=3
        )
        place.tags.add('I a tag', 'testlocation')
        place.save()

        other_user = User.objects.create_user(
            username='blubberbernd',
            password='Develop123'
        )

        superuser = User.objects.create_user(
            username='toor',
            password='Develop123'
        )

        superuser.is_superuser = True
        superuser.save()

        Place.objects.create(
            name='Im a own place',
            submitted_when=timezone.now(),
            submitted_by=other_user.explorer,
            location='Test %d town' % 5,
            latitude=50.5 + 5/10,
            longitude=7.0 - 5/10,
            description='This is just a test, do not worry %d' % 5,
            level=3
        )

    def test_not_eligible_to_see_because_of_low_level(self):
        self.client.login(username='blubberbernd', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))

        self.assertFalse(
            'Im a place' in response.content.decode(),
            msg='Expecting the user to not see the places'
        )

    def test_not_eligible_to_see_because_of_low_level_superuser(self):
        self.client.login(username='toor', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))

        self.assertTrue(
            'Im a place' in response.content.decode(),
            msg='Expecting the superuser to see all places'
        )

    def test_not_eligible_to_see_because_of_low_level_own_place(self):
        self.client.login(username='blubberbernd', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))

        self.assertTrue(
            'Im a own place' in response.content.decode(),
            msg='Expecting the user to see it\'s own places'
        )

    def test_eligible_to_see(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))

        self.assertTrue(
            'Im a own place' in response.content.decode(),
            msg='Expecting the user to see places where their level is high enough'
        )

    def test_not_authenticated(self):
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
        self.assertHttpRedirect(response)
        
        self.assertEqual(
            '%s?next=%s' % (
                reverse('login'),
                reverse('place_detail', kwargs={'pk': 1})
            ),
            response.url,
            msg='Expecting unauthenticated user to be redirected to login page, using the \'next\' GET-Param to store the places details page'
        )

    def test_context(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))

        self.assertTrue(
            'tagging_config' in response.context,
            msg='Expecting the context of %s to have an \'tagging_config\'' % (
                str(self.view)
            )
        )
        self.assertTaggableContext(response.context['tagging_config'])

        self.assertTrue(
            'mapping_config' in response.context,
            msg='Expecting the context of %s to have an \'mapping_config\'' % (
                str(self.view)
            )
        )
        self.assertMapableContext(response.context['mapping_config'])
        
    def test_place_data(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
        place = Place.objects.get(id=1)
        
        self.assertTrue(
            place.name in response.content.decode(),
            msg='Expecting the places name to be on the page'
        )
        self.assertTrue(
            place.description in response.content.decode(),
            msg='Expecting the places description to be on the page'
        )
        self.assertTrue(
            str(place.latitude) in response.content.decode(),
            msg='Expecting the places latitude to be on the page'
        )
        self.assertTrue(
            str(place.longitude) in response.content.decode(),
            msg='Expecting the places longitude to be on the page'
        )

    def test_favorite(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
        
        user = User.objects.get(username='testpeter')
        place = Place.objects.get(id=1)
        
        self.assertTrue(
            reverse('place_favorite', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) create favorite url to be on the page '
        )
        
        response = self.client.get(
            reverse('place_favorite', kwargs={'place_id': 1}),
            follow=True
        )
        self.assertHttpOK(response)
        
        self.assertTrue(
            reverse('place_unfavorite', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) delete favorite url to be on the page '
        )
        
        self.assertTrue(
            place in user.explorer.favorite_places.all(),
            msg='Expecting the place to be in the explorers favorites'
        )
        self.assertTrue(
            user.explorer in place.explorer_favorites.all(),
            msg='Expecting the explorere to be in the reverse list of favorites'
        )
        
        response = self.client.get(
            reverse('place_unfavorite', kwargs={'place_id': 1}),
            follow=True
        )
        self.assertHttpOK(response)
        self.assertTrue(
            reverse('place_favorite', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) create favorite url to be on the page after unfavorite'
        )
        
        self.assertTrue(
            place not in user.explorer.favorite_places.all(),
            msg='Expecting the place to not be in the explorers favorites after unfavorite'
        )
        self.assertTrue(
            user.explorer not in place.explorer_favorites.all(),
            msg='Expecting the explorere to not be in the reverse list of favorites after unfavorite'
        )
        
        
    def test_visit(self):
        self.client.login(username='testpeter', password='Develop123')
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
        
        user = User.objects.get(username='testpeter')
        place = Place.objects.get(id=1)
        
        self.assertTrue(
            reverse('place_visit_create', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) create visit url to be on the page '
        )
        
        response = self.client.get(
            reverse('place_visit_create', kwargs={'place_id': 1}),
            follow=True
        )
        self.assertHttpOK(response)
        
        self.assertTrue(
            reverse('place_visit_delete', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) delete visist url to be on the page '
        )
        
        self.assertTrue(
            place in user.explorer.visited_places.all(),
            msg='Expecting the place to be in the explorers visits'
        )
        self.assertTrue(
            user.explorer in place.explorer_visits.all(),
            msg='Expecting the explorer to be in the reverse list of visits'
        )
        
        response = self.client.get(
            reverse('place_visit_delete', kwargs={'place_id': 1}),
            follow=True
        )
        self.assertHttpOK(response)
        self.assertTrue(
            reverse('place_visit_create', kwargs={'place_id': 1}) in response.content.decode(),
            msg='Expecting the (right) create favorite url to be on the page after deleting visit'
        )
        
        self.assertTrue(
            place not in user.explorer.visited_places.all(),
            msg='Expecting the place to not be in the explorers visits after deleting visit'
        )
        self.assertTrue(
            user.explorer not in place.explorer_visits.all(),
            msg='Expecting the explorer to not be in the reverse list of visits after deleting visit'
        )
        
        
        