# -*- coding: utf-8 -*-
"""Tests for connectmessages.models."""
# pylint: disable=invalid-name, protected-access
from textwrap import dedent
import datetime

from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings
from django.utils.timezone import now
from mock import patch
from model_mommy import mommy
import pytz

from open_connect.connectmessages.models import Message, UserThread, Thread
from open_connect.connectmessages.tasks import send_system_message
from open_connect.connectmessages.tests import ConnectMessageTestCase
from open_connect.connect_core.utils.basetests import ConnectTestMixin


USER_MODEL = get_user_model()


class ThreadPublicManagerByUserTest(ConnectTestMixin, TestCase):
    """ThreadPublicManager.by_user tests."""
    def test_by_user_user_is_in_group(self):
        """Should return threads that user is a part of."""
        recipient = self.create_user()
        thread = self.create_thread(recipient=recipient)
        result = Thread.public.by_user(user=recipient)
        self.assertIn(thread, result)

    def test_by_user_user_is_not_in_group_or_recipient(self):
        """Should not have threads the user is not a part of."""
        thread = self.create_thread()
        user = self.create_user()
        result = Thread.public.by_user(user=user)
        self.assertNotIn(thread, result)

    def test_invisible_thread(self):
        """Should not have threads that are marked invisible."""
        thread = self.create_thread(visible=False)
        result = Thread.public.by_user(thread.recipients.first())
        self.assertNotIn(thread, result)

    def test_first_message_sender_is_banned(self):
        """Threads started by banned members should not be visible."""
        gro
up = mommy.make('groups.Group')

        normal_user = self.create_user()
        normal_user.add_to_group(group.pk)
        banned_user = self.create_user(is_banned=True)
        thread = self.create_thread(sender=banned_user, group=group)

        # normal_user should not see the thread from the banned user
        result = Thread.public.by_user(normal_user)
        self.assertNotIn(thread, result)

    def test_first_message_sender_is_banned_and_is_user(self):
        """Threads started by banned member are visible to the banned member."""
        group = mommy.make('groups.Group')
        banned_user = self.create_user(is_banned=True)
        banned_user.add_to_group(group.pk)
        thread = self.create_thread(sender=banned_user, group=group)

        result = Thread.public.by_user(banned_user)
        self.assertIn(thread, result)

    def test_non_active_userthread(self):
        """Userthreads that are not active should not be in the by_user"""
        user = self.create_user()
        thread = self.create_thread(recipient=user)

        UserThread.objects.filter(
            thread=thread, user=user).update(status='deleted')

        result = Thread.public.by_user(user)
        self.assertNotIn(thread, result)

    def test_userthread_status(self):
        """The current userthread_status should be selected."""
        thread = self.create_thread()
        thread = Thread.public.by_user(thread.test_shortcuts['recipient'])
        self.assertEqual(thread[0].userthread_status, 'active')


class ThreadPublicManagerByGroupTest(ConnectTestMixin, TestCase):
    """ThreadPublicManager.by_group tests."""
    def test_by_group(self):
        """Should return threads posted to the group."""
        thread = self.create_thread()
        result = Thread.public.by_group(thread.group)
        self.assertIn(thread, result)

    def test_by_group_no_messages_for_another_group(self):
        """Should not have threads posted to another group."""
        thread = self.create_thread()
        other_group = mommy.make('groups.Group')
        result = Thread.public.by_group(thread.group)
        self.assertNotIn(other_group, result)

    def test_invisible_thread(self):
        """Should not have threads that are marked invisible."""
        thread = self.create_thread(visible=False)
        result = Thread.public.by_group(thread.group)
        self.assertNotIn(thread, result)

    def test_first_message_sender_is_banned(self):
        """Threads started by banned members should not be visible."""
        banned_user = self.create_user(is_banned=True)
        thread = self.create_thread(sender=banned_user)
        result = Thread.public.by_group(thread.group)
        self.assertNotIn(thread, result)

    def test_group_is_private(self):
        """Private groups should not have their threads exposed."""
        group = mommy.make('groups.Group', private=True)
        thread = self.create_thread(group=group)
        result = Thread.public.by_group(group)
        self.assertNotIn(thread, result)


@override_settings(TIME_ZONE='US/Central')
class ThreadTest(ConnectTestMixin, TestCase):
    """Thread model tests."""
    def test_unicode(self):
        """Unicode conversion should be Thread and the id of the thread."""
        thread = mommy.prepare('connectmessages.Thread')
        self.assertEqual(str(thread), "Thread %s" % thread.subject)

    def test_add_user_to_thread(self):
        """Test that add_user_to_thread adds the user to the thread."""
        thread = self.create_thread()
        user = self.create_user()
        thread.add_user_to_thread(user)
        self.assertTrue(
            UserThread.objects.filter(thread=thread, user=user).exists())

    def test_get_absolute_url(self):
        """Test get_absolute_url"""
        thread = self.create_thread()
        self.assertEqual(
            thread.get_absolute_url(),
            '/messages/id/{pk}/'.format(
                pk=thread.pk)
        )

    def test_get_unsubscribe_url(self):
        """Test the get_unsubscribe_url method on the Thread model"""
        thread = self.create_thread()
        self.assertEqual(
            thread.get_unsubscribe_url(),
            reverse('thread_unsubscribe', args=[thread.pk])
        )

    def test_group_serializable(self):
        """Test the thread serialization method for a group message"""
        sender = self.create_user()
        thread = self.create_thread(sender=sender)

        message = thread.first_message
        chicago = pytz.timezone('US/Central')
        self.assertDictEqual(
            thread.serializable(),
            {
                'id': thread.pk,
                'total_messages': '1',
                'json_url': reverse('thread_details_json', args=[thread.pk]),
                'subject': thread.subject,
                'snippet': unicode(message.snippet),
                'read': None,
                'group': unicode(thread.group),
                'group_url': reverse(
                    'group_details', kwargs={'pk': thread.group.pk}),
                'group_id': thread.group.pk,
                'type': 'group',
                'unread_messages': 1,
                'category': str(thread.group.category.slug),
                'reply_url': reverse(
                    'create_reply', args=[thread.pk]),
                'unsubscribe_url': thread.get_unsubscribe_url(),
                'is_system_thread': False,
                'userthread_status': None,
                'latest_message_at':
                    str(thread.latest_message.created_at.astimezone(chicago))
            }
        )

    def test_direct_serializable(self):
        """Test the thread serialization method for a direct message"""
        sender = self.create_user()
        recipient = self.create_user()
        thread = self.create_thread(
            direct=True, sender=sender, recipient=recipient)

        message = thread.first_message
        chicago = pytz.timezone('US/Central')
        self.assertDictItemsEqualUnordered(
            thread.serializable(),

            {
                'id': thread.pk,
                'total_messages': '1',
                'subject': thread.subject,
                'snippet': unicode(message.snippet),
                'recipients': [
                    str(sender),
   