from misago.acl.testutils import override_acl
from misago.categories.models import Category
from misago.users.testutils import AuthenticatedUserTestCase

from .. import testutils
from ..models import Post, Thread
from ..moderation import threads as threads_moderation
from ..moderation.posts import hide_post


class MockRequest(object):
    def __init__(self, user):
        self.user = user
        self.user_ip = '127.0.0.1'


class ThreadViewTestCase(AuthenticatedUserTestCase):
    def setUp(self):
        super(ThreadViewTestCase, self).setUp()

        self.category = Category.objects.get(slug='first-category')
        self.thread = testutils.post_thread(category=self.category)

    def override_acl(self, acl=None):
        final_acl = {
            'can_see': 1,
            'can_browse': 1,
            'can_see_all_threads': 1,
            'can_see_own_threads': 0,
            'can_hide_threads': 0,
            'can_approve_content': 0,
            'can_edit_posts': 0,
            'can_hide_posts': 0,
            'can_hide_own_posts': 0,
            'can_close_threads': 0,
            'post_edit_time': 0,
            'can_hide_events': 0,
        }

        if acl:
            final_acl.update(acl)

        override_acl(self.user, {
            'categories': {
                self.category.pk: final_acl
            }
        })


class ThreadVisibilityTests(ThreadViewTestCase):
    def test_thread_displays(self):
        """thread view has no showstoppers"""
        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, self.thread.title)

    def test_view_shows_owner_thread(self):
        """view handles "owned threads only" """
        self.override_acl({
            'can_see_all_threads': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

        self.thread.starter = self.user
        self.thread.save()

        self.override_acl({
            'can_see_all_threads': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, self.thread.title)

    def test_view_validates_category_permissions(self):
        """view validates category visiblity"""
        self.override_acl({
            'can_see': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

        self.override_acl({
            'can_browse': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

    def test_view_shows_unapproved_thread(self):
        """view handles unapproved thread"""
        self.override_acl({
            'can_approve_content': 0
        })

        self.thread.is_unapproved = True
        self.thread.save()

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

        # grant permission to see unapproved content
        self.override_acl({
            'can_approve_content': 1
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, self.thread.title)

        # make test user thread's owner and remove permission to see unapproved
        # user should be able to see thread as its author anyway
        self.thread.starter = self.user
        self.thread.save()

        self.override_acl({
            'can_approve_content': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, self.thread.title)

    def test_view_shows_hidden_thread(self):
        """view handles hidden thread"""
        self.override_acl({
            'can_hide_threads': 0
        })

        self.thread.is_hidden = True
        self.thread.save()

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

        # threads owners are not extempt from hidden threads check
        self.thread.starter = self.user
        self.thread.save()

        response = self.client.get(self.thread.get_absolute_url())
        self.assertEqual(response.status_code, 404)

        # grant permission to see hidden content
        self.override_acl({
            'can_hide_threads': 1
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, self.thread.title)


class ThreadPostsVisibilityTests(ThreadViewTestCase):
    def test_post_renders(self):
        """post renders"""
        post = testutils.reply_thread(self.thread, poster=self.user)

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())

    def test_invalid_post_renders(self):
        """invalid post renders"""
        post = testutils.reply_thread(self.thread, poster=self.user)

        post.parsed = 'fiddled post content'
        post.save()

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post's contents cannot be displayed.")
        self.assertNotContains(response, post.parsed)

    def test_hidden_post_visibility(self):
        """hidden post renders correctly"""
        post = testutils.reply_thread(self.thread, message="Hello, I'm hidden post!")
        hide_post(self.user, post)

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is hidden. You cannot not see its contents.")
        self.assertNotContains(response, post.parsed)

        # posts authors are not extempt from seeing hidden posts content
        post.posted_by = self.user
        post.save()

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is hidden. You cannot not see its contents.")
        self.assertNotContains(response, post.parsed)

        # permission to hide own posts isn't enought to see post content
        self.override_acl({
            'can_hide_own_posts': 1
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is hidden. You cannot not see its contents.")
        self.assertNotContains(response, post.parsed)

        # post's content is displayed after permission to see posts is granted
        self.override_acl({
            'can_hide_posts': 1
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is hidden. Only users with permission may see its contents.")
        self.assertNotContains(response, "This post is hidden. You cannot not see its contents.")
        self.assertContains(response, post.parsed)

    def test_unapproved_post_visibility(self):
        """unapproved post renders for its author and users with perm to approve content"""
        post = testutils.reply_thread(self.thread, is_unapproved=True)

        # post is hdden because we aren't its author nor user with permission to approve
        response = self.client.get(self.thread.get_absolute_url())
        self.assertNotContains(response, post.get_absolute_url())

        # post displays because we have permission to approve unapproved content
        self.override_acl({
            'can_approve_content': 1
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is unapproved.")
        self.assertContains(response, post.parsed)

        # post displays because we are its author
        post.poster = self.user
        post.save()

        self.override_acl({
            'can_approve_content': 0
        })

        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, post.get_absolute_url())
        self.assertContains(response, "This post is unapproved.")
        self.assertContains(response, post.parsed)


class ThreadEventVisibilityTests(ThreadViewTestCase):
    def test_thread_events_render(self):
        """different thread events render"""
        TEST_ACTIONS = (
            (threads_moderation.pin_thread_globally, "Thread has been pinned globally."),
            (threads_moderation.pin_thread_locally, "Thread has been pinned locally."),
            (threads_moderation.unpin_thread, "Thread has been unpinned."),
            (threads_moderation.approve_thread, "Thread has been approved."),
            (threads_moderation.close_thread, "Thread has been closed."),
            (threads_moderation.open_thread, "Thread has been opened."),
            (threads_moderation.hide_thread, "Thread has been made hidden."),
            (threads_moderation.unhide_thread, "Thread has been revealed."),
        )

        self.thread.is_unapproved = True
        self.thread.save()

        for action, message in TEST_ACTIONS:
            self.override_acl({
                'can_approve_content': 1,
                'can_hide_threads': 1,
            })

            self.thread.post_set.filter(is_event=True).delete()
            action(MockRequest(self.user), self.thread)

            event = self.thread.post_set.filter(is_event=True)[0]

            # event renders
            response = self.client.get(self.thread.get_absolute_url())
            self.assertContains(response, event.get_absolute_url())
            self.assertContains(response, message)

            # hidden events don't render without permission
            hide_post(self.user, event)
            self.override_acl({
                'can_approve_content': 1,
                'can_hide_threads': 1,
            })

            response = self.client.get(self.thread.get_absolute_url())
            self.assertNotContains(response, event.get_absolute_url())
            self.assertNotContains(response, message)

            # hidden event renders with permission
            hide_post(self.user, event)
            self.override_acl({
                'can_approve_content': 1,
                'can_hide_threads': 1,
                'can_hide_events': 1,
            })

            response = self.client.get(self.thread.get_absolute_url())
            self.assertContains(response, event.get_absolute_url())
            self.assertContains(response, message)
            self.assertContains(response, "Hidden by")

    def test_thread_move_event_renders(self):
        """moved thread event renders"""
        self.thread.category = self.thread.category.parent
        self.thread.save()

        threads_moderation.move_thread(MockRequest(self.user), self.thread, self.category)

        event = self.thread.post_set.filter(is_event=True)[0]
        self.assertEqual(event.event_type, 'moved')

        # event renders
        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, event.get_absolute_url())
        self.assertContains(response, "Thread has been moved from")

    def test_thread_merged_event_renders(self):
        """merged thread event renders"""
        other_thread = testutils.post_thread(category=self.category)
        threads_moderation.merge_thread(MockRequest(self.user), self.thread, other_thread)

        event = self.thread.post_set.filter(is_event=True)[0]
        self.assertEqual(event.event_type, 'merged')

        # event renders
        response = self.client.get(self.thread.get_absolute_url())
        self.assertContains(response, event.get_absolute_url())
        self.assertContains(response, "thread has been merged into this thread")