import urlparse
import threading
from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey
from django.conf import settings
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from misago.signals import delete_forum_content, move_forum_content, rename_forum, rename_user

thread_local = threading.local()

class ForumManager(TreeManager):
    @property
    def forums_tree(self):
        try:
            return thread_local.misago_forums_tree
        except AttributeError:
            thread_local.misago_forums_tree = None
        return thread_local.misago_forums_tree

    @forums_tree.setter
    def forums_tree(self, value):
        thread_local.misago_forums_tree = value

    def special_pk(self, name):
        self.populate_tree()
        return self.forums_tree.get(name).pk

    def special_model(self, name):
        self.populate_tree()
        return self.forums_tree.get(name)

    def populate_tree(self, force=False):
        if not self.forums_tree:
            self.forums_tree = cache.get('forums_tree')
        if not self.forums_tree or force:
            self.forums_tree = {}
            for forum in Forum.objects.order_by('lft'):
                self.forums_tree[forum.pk] = forum
                if forum.special:
                    self.forums_tree[forum.special] = forum
            cache.set('forums_tree', self.forums_tree)

    def forum_parents(self, forum, include_self=False):
        self.populate_tree()
        parents = []
        parent = self.forums_tree[forum]
        if include_self:
            parents.append(parent)
        while parent.level > 1:
            parent = self.forums_tree[parent.parent_id]
            parents.append(parent)
        result = []
        for i in reversed(parents):
            result.append(i)
        return list(result)

    def parents_aware_forum(self, forum):
        self.populate_tree()
        proxy = Forum()
        try:
            proxy.id = forum.pk
            proxy.pk = forum.pk
        except AttributeError:
            proxy.id = forum
            proxy.pk = forum
        proxy.closed = False
        for parent in self.forum_parents(proxy.pk):
            if parent.closed:
                proxy.closed = True
                return proxy
        return proxy

    def treelist(self, acl, parent=None, tracker=None):
        complete_list = []
        forums_list = []
        parents = {}

        if parent:
            queryset = Forum.objects.filter(pk__in=acl.known_forums).filter(lft__gt=parent.lft).filter(rght__lt=parent.rght).order_by('lft')
        else:
            queryset = Forum.objects.filter(pk__in=acl.known_forums).order_by('lft')

        for forum in queryset:
            forum.subforums = []
            forum.is_read = False
            if tracker:
                forum.is_read = tracker.is_read(forum)
            parents[forum.pk] = forum
            complete_list.append(forum)
            if forum.parent_id in parents:
                parents[forum.parent_id].subforums.append(forum)
            else:
                forums_list.append(forum)

        # Second iteration - sum up forum counters
        for forum in reversed(complete_list):
            if forum.parent_id in parents and parents[forum.parent_id].type != 'redirect':
                parents[forum.parent_id].threads += forum.threads
                parents[forum.parent_id].posts += forum.posts
                if acl.can_browse(forum.pk):
                    # If forum is unread, make parent unread too
                    if not forum.is_read:
                        parents[forum.parent_id].is_read = False
                    # Sum stats
                    if forum.last_thread_date and (not parents[forum.parent_id].last_thread_date or forum.last_thread_date > parents[forum.parent_id].last_thread_date):
                        parents[forum.parent_id].last_thread_id = forum.last_thread_id
                        parents[forum.parent_id].last_thread_name = forum.last_thread_name
                        parents[forum.parent_id].last_thread_slug = forum.last_thread_slug
                        parents[forum.parent_id].last_thread_date = forum.last_thread_date
                        parents[forum.parent_id].last_poster_id = forum.last_poster_id
                        parents[forum.parent_id].last_poster_name = forum.last_poster_name
                        parents[forum.parent_id].last_poster_slug = forum.last_poster_slug
                        parents[forum.parent_id].last_poster_style = forum.last_poster_style
        return forums_list
    
    def ignored_users(self, user, forums):
        check_ids = []
        for forum in forums:
            forum.last_poster_ignored = False
            if user.is_authenticated() and user.pk != forum.last_poster_id and forum.last_poster_id and not forum.last_poster_id in check_ids:
                check_ids.append(forum.last_poster_id)
        ignored_ids = []
        if check_ids and user.is_authenticated():
            for user in user.ignores.filter(id__in=check_ids).values('id'):
                ignored_ids.append(user['id'])

    def readable_forums(self, acl, include_special=False):
        self.populate_tree()
        readable = []
        for pk, forum in self.forums_tree.items():
            if ((include_special or not forum.special) and 
                    acl.forums.can_browse(forum.pk) and
                    acl.threads.acl[forum.pk]['can_read_threads']):
                readable.append(forum.pk)
        return readable


class Forum(MPTTModel):
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
    type = models.CharField(max_length=12)
    special = models.CharField(max_length=255, null=True, blank=True)
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    description = models.TextField(null=True, blank=True)
    description_preparsed = models.TextField(null=True, blank=True)
    threads = models.PositiveIntegerField(default=0)
    threads_delta = models.PositiveIntegerField(default=0)
    posts = models.PositiveIntegerField(default=0)
    posts_delta = models.IntegerField(default=0)
    redirects = models.PositiveIntegerField(default=0)
    redirects_delta = models.IntegerField(default=0)
    last_thread = models.ForeignKey('Thread', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
    last_thread_name = models.CharField(max_length=255, null=True, blank=True)
    last_thread_slug = models.SlugField(max_length=255, null=True, blank=True)
    last_thread_date = models.DateTimeField(null=True, blank=True)
    last_poster = models.ForeignKey('User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
    last_poster_name = models.CharField(max_length=255, null=True, blank=True)
    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
    last_poster_style = models.CharField(max_length=255, null=True, blank=True)
    prune_start = models.PositiveIntegerField(default=0)
    prune_last = models.PositiveIntegerField(default=0)
    pruned_archive = models.ForeignKey('self', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
    redirect = models.CharField(max_length=255, null=True, blank=True)
    attrs = models.CharField(max_length=255, null=True, blank=True)
    show_details = models.BooleanField(default=True)
    style = models.CharField(max_length=255, null=True, blank=True)
    closed = models.BooleanField(default=False)

    objects = ForumManager()

    class Meta:
        app_label = 'misago'
    
    def save(self, *args, **kwargs):
        super(Forum, self).save(*args, **kwargs)
        cache.delete('forums_tree')
    
    def delete(self, *args, **kwargs):
        delete_forum_content.send(sender=self)
        super(Forum, self).delete(*args, **kwargs)
        cache.delete('forums_tree')

    def __unicode__(self):
        if self.special == 'private_threads':
           return unicode(_('Private Threads'))
        if self.special == 'reports':
           return unicode(_('Reports'))
        if self.special == 'root':
           return unicode(_('Root Category'))
        return unicode(self.name)

    def set_description(self, description):
        self.description = description.strip()
        self.description_preparsed = ''
        if self.description:
            import markdown
            self.description_preparsed = markdown.markdown(description, safe_mode='escape', output_format=settings.OUTPUT_FORMAT)

    def copy_permissions(self, target):
        if target.pk != self.pk:
            from misago.models import Role
            for role in Role.objects.all():
                perms = role.permissions
                try:
                    perms['forums'][self.pk] = perms['forums'][target.pk]
                    role.permissions = perms
                    role.save(force_update=True)
                except KeyError:
                    pass

    def move_content(self, target):
        move_forum_content.send(sender=self, move_to=target)

    def sync_name(self):
        rename_forum.send(sender=self)

    def attr(self, att):
        if self.attrs:
            return att in self.attrs.split()
        return False

    def redirect_domain(self):
        hostname = urlparse.urlparse(self.redirect).hostname
        scheme = urlparse.urlparse(self.redirect).scheme
        if scheme:
            scheme = '%s://' % scheme
        return '%s%s' % (scheme, hostname)

    def new_last_thread(self, thread):
        self.last_thread = thread
        self.last_thread_name = thread.name
        self.last_thread_slug = thread.slug
        self.last_thread_date = thread.last
        self.last_poster = thread.last_poster
        self.last_poster_name = thread.last_poster_name
        self.last_poster_slug = thread.last_poster_slug
        self.last_poster_style = thread.last_poster_style

    def sync(self):
        self.threads = self.thread_set.filter(moderated=False).filter(deleted=False).count()
        self.posts = self.post_set.filter(moderated=False).count()
        self.last_poster = None
        self.last_poster_name = None
        self.last_poster_slug = None
        self.last_poster_style = None
        self.last_thread = None
        self.last_thread_date = None
        self.last_thread_name = None
        self.last_thread_slug = None
        try:
            last_thread = self.thread_set.filter(moderated=False).filter(deleted=False).order_by('-last').all()[0:][0]
            self.last_poster_name = last_thread.last_poster_name
            self.last_poster_slug = last_thread.last_poster_slug
            self.last_poster_style = last_thread.last_poster_style
            if last_thread.last_poster:
                self.last_poster = last_thread.last_poster
            self.last_thread = last_thread
            self.last_thread_date = last_thread.last
            self.last_thread_name = last_thread.name
            self.last_thread_slug = last_thread.slug
        except (IndexError, AttributeError):
            pass

    def prune(self):
        pass


"""
Signals
"""
def rename_user_handler(sender, **kwargs):
    Forum.objects.filter(last_poster=sender).update(
                                                    last_poster_name=sender.username,
                                                    last_poster_slug=sender.username_slug,
                                                    )

rename_user.connect(rename_user_handler, dispatch_uid='rename_forums_last_poster')