from json import loads
from datetime import datetime
from inspect import getargspec

from django.apps import apps
from django.test import TestCase
from django.template import Template, Context
from django.utils.six import text_type
from django.utils.timesince import timesince
from django.contrib.sites.models import Site
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType

try:
    from django.core.urlresolvers import reverse
except ImportError:
    from django.urls import reverse

from actstream.models import Action, Follow
from actstream.registry import register, unregister
from actstream.actions import follow
from actstream.signals import action


def render(src, **ctx):
    return Template('{% load activity_tags %}' + src).render(Context(ctx))


class LTE(int):
    def __new__(cls, n):
        obj = super(LTE, cls).__new__(cls, n)
        obj.n = n
        return obj

    def __eq__(self, other):
        return other <= self.n

    def __repr__(self):
        return "<= %s" % self.n


class ActivityBaseTestCase(TestCase):
    actstream_models = ()
    maxDiff = None

    def setUp(self):
        self.User = get_user_model()
        self.user_ct = ContentType.objects.get_for_model(self.User)
        register(self.User)
        for model in self.actstream_models:
            register(model)

    def assertSetEqual(self, l1, l2, msg=None, domap=True):
        if domap:
            l1 = map(text_type, l1)
        self.assertSequenceEqual(set(l1), set(l2), msg)

    def assertAllIn(self, bits, string):
        for bit in bits:
            self.assertIn(bit, string)

    def assertJSON(self, string):
        return loads(string)

    def tearDown(self):
        for model in self.actstream_models:
            model = apps.get_model(*model.split('.'))
            unregister(model)
            model.objects.all().delete()
        Action.objects.all().delete()
        Follow.objects.all().delete()
        self.User.objects.all().delete()

    def capture(self, viewname, *args):
        response = self.client.get(reverse(viewname, args=args))
        content = response.content.decode()
        if response['Content-Type'] == 'application/json':
            return loads(content)
        return content


class DataTestCase(ActivityBaseTestCase):
    actstream_models = ('auth.Group', 'sites.Site')

    def setUp(self):
        self.testdate = datetime(2000, 1, 1)
        self.timesince = timesince(self.testdate).encode('utf8').replace(
            b'\xc2\xa0', b' ').decode()
        self.group_ct = ContentType.objects.get_for_model(Group)
        super(DataTestCase, self).setUp()
        self.group = Group.objects.create(name='CoolGroup')
        self.another_group = Group.objects.create(name='NiceGroup')
        if 'email' in getargspec(self.User.objects.create_superuser).args:
            self.user1 = self.User.objects.create_superuser('admin', 'admin@example.com', 'admin')
            self.user2 = self.User.objects.create_user('Two', 'two@example.com')
            self.user3 = self.User.objects.create_user('Three', 'three@example.com')
            self.user4 = self.User.objects.create_user('Four', 'four@example.com')
        else:
            self.user1 = self.User.objects.create_superuser('admin', 'admin')
            self.user2 = self.User.objects.create_user('Two')
            self.user3 = self.User.objects.create_user('Three')
            self.user4 = self.User.objects.create_user('Four')
        # User1 joins group
        self.user1.groups.add(self.group)
        self.join_action = action.send(self.user1, verb='joined',
                                       target=self.group,
                                       timestamp=self.testdate)[0][1]

        # User1 follows User2
        follow(self.user1, self.user2, timestamp=self.testdate)

        # User2 joins group
        self.user2.groups.add(self.group)
        action.send(self.user2, verb='joined', target=self.group,
                    timestamp=self.testdate)

        # User2 follows group
        follow(self.user2, self.group, timestamp=self.testdate)

        # User1 comments on group
        # Use a site object here and predict the "__unicode__ method output"
        action.send(self.user1, verb='commented on', target=self.group,
                    timestamp=self.testdate)

        self.comment = Site.objects.create(
            domain="admin: Sweet Group!...")

        # Group responds to comment
        action.send(self.group, verb='responded to', target=self.comment,
                    timestamp=self.testdate)

        # User 3 did something but doesn't following someone
        action.send(self.user3, verb='liked actstream', timestamp=self.testdate)

        # User4 likes group
        follow(self.user4, self.another_group, timestamp=self.testdate, flag='liking')
        # User4 watches group
        follow(self.user4, self.another_group, timestamp=self.testdate, flag='watching')
        # User4 likes User1
        follow(self.user4, self.user1, timestamp=self.testdate, flag='liking')
        # User4 blacklist user3
        follow(self.user4, self.user3, timestamp=self.testdate, flag='blacklisting')
