from django.contrib import messages
from django.db.transaction import atomic
from django.http import Http404
from django.shortcuts import redirect, render
from django.utils import timezone
from django.utils.translation import ungettext, ugettext_lazy, ugettext as _

from misago.forums.lists import get_forum_path

from misago.threads import moderation
from misago.threads.forms.moderation import MovePostsForm, SplitThreadForm
from misago.threads.models import Thread
from misago.threads.paginator import Paginator
from misago.threads.views.generic.actions import ActionsBase, ReloadAfterDelete


__all__ = ['PostsActions']


def thread_aware_posts(f):
    def decorator(self, request, posts):
        for post in posts:
            post.thread = self.thread

        return f(self, request, posts)
    return decorator


def changes_thread_state(f):
    @thread_aware_posts
    def decorator(self, request, posts):
        with atomic():
            self.thread.lock()

            response = f(self, request, posts)

            self.thread.synchronize()
            self.thread.save()

            self.forum.lock()
            self.forum.synchronize()
            self.forum.save()

            return response
    return decorator


class PostsActions(ActionsBase):
    select_items_message = ugettext_lazy(
        "You have to select at least one post.")
    is_mass_action = True

    def redirect_after_deletion(self, request, queryset):
        paginator = Paginator(queryset, 10, 3)
        current_page = int(request.resolver_match.kwargs.get('page', 0))

        if paginator.num_pages < current_page:
            namespace = request.resolver_match.namespace
            url_name = request.resolver_match.url_name
            kwars = request.resolver_match.kwargs
            kwars['page'] = paginator.num_pages
            if kwars['page'] == 1:
                del kwars['page']
            return redirect('%s:%s' % (namespace, url_name), **kwars)
        else:
            return redirect(request.path)

    def get_available_actions(self, kwargs):
        self.thread = kwargs['thread']
        self.forum = self.thread.forum

        actions = []

        if self.thread.acl['can_review']:
            if self.thread.has_moderated_posts:
                actions.append({
                    'action': 'approve',
                    'icon': 'check',
                    'name': _("Approve posts")
                })

        if self.forum.acl['can_merge_posts']:
            actions.append({
                'action': 'merge',
                'icon': 'compress',
                'name': _("Merge posts into one")
            })

        if self.forum.acl['can_move_posts']:
            actions.append({
                'action': 'move',
                'icon': 'arrow-right',
                'name': _("Move posts to other thread")
            })

        if self.forum.acl['can_split_threads']:
            actions.append({
                'action': 'split',
                'icon': 'code-fork',
                'name': _("Split posts to new thread")
            })

        if self.forum.acl['can_protect_posts']:
            actions.append({
                'action': 'unprotect',
                'icon': 'unlock-alt',
                'name': _("Release posts")
            })
            actions.append({
                'action': 'protect',
                'icon': 'lock',
                'name': _("Protect posts")
            })

        if self.forum.acl['can_hide_posts']:
            actions.append({
                'action': 'unhide',
                'icon': 'eye',
                'name': _("Reveal posts")
            })
            actions.append({
                'action': 'hide',
                'icon': 'eye-slash',
                'name': _("Hide posts")
            })
        if self.forum.acl['can_hide_posts'] == 2:
            actions.append({
                'action': 'delete',
                'icon': 'times',
                'name': _("Delete posts"),
                'confirmation': _("Are you sure you want to delete selected "
                                  "posts? This action can't be undone.")
            })

        return actions

    @changes_thread_state
    def action_approve(self, request, posts):
        changed_posts = 0
        for post in posts:
            if moderation.approve_post(request.user, post):
                changed_posts += 1

        if changed_posts:
            message = ungettext(
                '%(changed)d post was approved.',
                '%(changed)d posts were approved.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
        else:
            message = _("No posts were approved.")
            messages.info(request, message)

    @changes_thread_state
    def action_merge(self, request, posts):
        first_post = posts[0]

        changed_posts = len(posts)
        if changed_posts < 2:
            message = _("You have to select at least two posts to merge.")
            raise moderation.ModerationError(message)

        for post in posts:
            if not post.poster_id or first_post.poster_id != post.poster_id:
                message = _("You can't merge posts made by different authors.")
                raise moderation.ModerationError(message)

        for post in posts[1:]:
            post.merge(first_post)
            post.delete()

        first_post.save()

        message = ungettext(
            '%(changed)d post was merged.',
            '%(changed)d posts were merged.',
        changed_posts)
        messages.success(request, message % {'changed': changed_posts})

    move_posts_full_template = 'misago/thread/move_posts/full.html'
    move_posts_modal_template = 'misago/thread/move_posts/modal.html'

    @changes_thread_state
    def action_move(self, request, posts):
        if posts[0].id == self.thread.first_post_id:
            message = _("You can't move thread's first post.")
            raise moderation.ModerationError(message)

        form = MovePostsForm(user=request.user, thread=self.thread)

        if 'submit' in request.POST or 'follow' in request.POST:
            form = MovePostsForm(request.POST,
                                 user=request.user,
                                 thread=self.thread)
            if form.is_valid():
                for post in posts:
                    post.move(form.new_thread)
                    post.save()

                form.new_thread.lock()
                form.new_thread.synchronize()
                form.new_thread.save()

                if form.new_thread.forum != self.forum:
                    form.new_thread.forum.lock()
                    form.new_thread.forum.synchronize()
                    form.new_thread.forum.save()

                changed_posts = len(posts)
                message = ungettext(
                    '%(changed)d post was moved to "%(thread)s".',
                    '%(changed)d posts were moved to "%(thread)s".',
                changed_posts)
                messages.success(request, message % {
                    'changed': changed_posts,
                    'thread': form.new_thread.title
                })

                if 'follow' in request.POST:
                    return redirect(form.new_thread.get_absolute_url())
                else:
                    return None # trigger thread refresh

        if request.is_ajax():
            template = self.move_posts_modal_template
        else:
            template = self.move_posts_full_template

        return render(request, template, {
            'form': form,
            'forum': self.forum,
            'thread': self.thread,
            'path': get_forum_path(self.forum),

            'posts': posts
        })

    split_thread_full_template = 'misago/thread/split/full.html'
    split_thread_modal_template = 'misago/thread/split/modal.html'

    @changes_thread_state
    def action_split(self, request, posts):
        if posts[0].id == self.thread.first_post_id:
            message = _("You can't split thread's first post.")
            raise moderation.ModerationError(message)

        form = SplitThreadForm(acl=request.user.acl)

        if 'submit' in request.POST or 'follow' in request.POST:
            form = SplitThreadForm(request.POST, acl=request.user.acl)
            if form.is_valid():
                split_thread = Thread()
                split_thread.forum = form.cleaned_data['forum']
                split_thread.set_title(
                    form.cleaned_data['thread_title'])
                split_thread.starter_name = "-"
                split_thread.starter_slug = "-"
                split_thread.last_poster_name = "-"
                split_thread.last_poster_slug = "-"
                split_thread.started_on = timezone.now()
                split_thread.last_post_on = timezone.now()
                split_thread.save()

                for post in posts:
                    post.move(split_thread)
                    post.save()

                split_thread.synchronize()
                split_thread.save()

                if split_thread.forum != self.forum:
                    split_thread.forum.lock()
                    split_thread.forum.synchronize()
                    split_thread.forum.save()

                changed_posts = len(posts)
                message = ungettext(
                    '%(changed)d post was split to "%(thread)s".',
                    '%(changed)d posts were split to "%(thread)s".',
                changed_posts)
                messages.success(request, message % {
                    'changed': changed_posts,
                    'thread': split_thread.title
                })

                if 'follow' in request.POST:
                    return redirect(split_thread.get_absolute_url())
                else:
                    return None # trigger thread refresh

        if request.is_ajax():
            template = self.split_thread_modal_template
        else:
            template = self.split_thread_full_template

        return render(request, template, {
            'form': form,
            'forum': self.forum,
            'thread': self.thread,
            'path': get_forum_path(self.forum),

            'posts': posts
        })

    def action_unprotect(self, request, posts):
        changed_posts = 0
        for post in posts:
            if moderation.unprotect_post(request.user, post):
                changed_posts += 1

        if changed_posts:
            message = ungettext(
                '%(changed)d post was released from protection.',
                '%(changed)d posts were released from protection.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
        else:
            message = _("No posts were released from protection.")
            messages.info(request, message)

    def action_protect(self, request, posts):
        changed_posts = 0
        for post in posts:
            if moderation.protect_post(request.user, post):
                changed_posts += 1

        if changed_posts:
            message = ungettext(
                '%(changed)d post was made protected.',
                '%(changed)d posts were made protected.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
        else:
            message = _("No posts were made protected.")
            messages.info(request, message)

    @changes_thread_state
    def action_unhide(self, request, posts):
        changed_posts = 0
        for post in posts:
            if moderation.unhide_post(request.user, post):
                changed_posts += 1

        if changed_posts:
            message = ungettext(
                '%(changed)d post was made visible.',
                '%(changed)d posts were made visible.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
        else:
            message = _("No posts were made visible.")
            messages.info(request, message)

    @changes_thread_state
    def action_hide(self, request, posts):
        changed_posts = 0
        for post in posts:
            if moderation.hide_post(request.user, post):
                changed_posts += 1

        if changed_posts:
            message = ungettext(
                '%(changed)d post was hidden.',
                '%(changed)d posts were hidden.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
        else:
            message = _("No posts were hidden.")
            messages.info(request, message)

    @changes_thread_state
    def action_delete(self, request, posts):
        changed_posts = 0
        first_deleted = None

        for post in posts:
            if moderation.delete_post(request.user, post):
                changed_posts += 1
                if not first_deleted:
                    first_deleted = post

        if changed_posts:
            message = ungettext(
                '%(changed)d post was deleted.',
                '%(changed)d posts were deleted.',
            changed_posts)
            messages.success(request, message % {'changed': changed_posts})
            return ReloadAfterDelete()
        else:
            message = _("No posts were deleted.")
            messages.info(request, message)