from __future__ import unicode_literals

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import TestCase
from django.test.client import RequestFactory
from django.views.generic import View
from django.views.generic import ListView

from guardian.shortcuts import assign_perm
import mock
from guardian.mixins import LoginRequiredMixin
from guardian.mixins import PermissionRequiredMixin
from guardian.mixins import PermissionListMixin

from ..models import Post


class DatabaseRemovedError(Exception):
    pass


class RemoveDatabaseView(View):

    def get(self, request, *args, **kwargs):
        raise DatabaseRemovedError("You've just allowed db to be removed!")


class TestView(PermissionRequiredMixin, RemoveDatabaseView):
    permission_required = 'testapp.change_post'
    object = None  # should be set at each tests explicitly


class NoObjectView(PermissionRequiredMixin, RemoveDatabaseView):
    permission_required = 'testapp.change_post'


class GlobalNoObjectView(PermissionRequiredMixin, RemoveDatabaseView):
    permission_required = 'testapp.add_post'
    accept_global_perms = True


class PostPermissionListView(PermissionListMixin, ListView):
    model = Post
    permission_required = 'testapp.change_post'
    template_name = 'list.html'


class TestViewMixins(TestCase):

    def setUp(self):
        self.post = Post.objects.create(title='foo-post-title')
        self.factory = RequestFactory()
        self.user = get_user_model().objects.create_user(
            'joe', 'joe@doe.com', 'doe')
        self.client.login(username='joe', password='doe')

    def test_permission_is_checked_before_view_is_computed(self):
        """
        This test would fail if permission is checked **after** view is
        actually resolved.
        """
        request = self.factory.get('/')
        request.user = self.user
        # View.object is set
        view = TestView.as_view(object=self.post)
        response = view(request)
        self.assertEqual(response.status_code, 302)

        # View.get_object returns object
        TestView.get_object = lambda instance: self.post
        view = TestView.as_view()
        response = view(request)
        self.assertEqual(response.status_code, 302)
        del TestView.get_object

    def test_permission_is_checked_before_view_is_computed_perm_denied_raised(self):
        """
        This test would fail if permission is checked **after** view is
        actually resolved.
        """
        request = self.factory.get('/')
        request.user = self.user
        view = TestView.as_view(raise_exception=True, object=self.post)
        with self.assertRaises(PermissionDenied):
            view(request)

    def test_permission_required_view_configured_wrongly(self):
        """
        This test would fail if permission is checked **after** view is
        actually resolved.
        """
        request = self.factory.get('/')
        request.user = self.user
        request.user.add_obj_perm('change_post', self.post)
        view = TestView.as_view(permission_required=None, object=self.post)
        with self.assertRaises(ImproperlyConfigured):
            view(request)

    def test_permission_required(self):
        """
        This test would fail if permission is checked **after** view is
        actually resolved.