import datetime
import json

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.forms.formsets import formset_factory
from django.shortcuts import get_object_or_404, redirect
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext, ungettext

import six

from olympia import amo
from olympia.access import acl
from olympia.activity.models import ActivityLog
from olympia.addons.models import Addon, Persona
from olympia.amo.decorators import json_view, post_required
from olympia.amo.urlresolvers import reverse
from olympia.amo.utils import paginate, render
from olympia.reviewers import forms
from olympia.reviewers.models import (
    RereviewQueueTheme, ReviewerScore, ThemeLock)
from olympia.reviewers.views import base_context as context
from olympia.users.models import UserProfile

from .decorators import personas_reviewer_required


QUEUE_PER_PAGE = 100


@personas_reviewer_required
def themes_list(request, flagged=False, rereview=False):
    """Themes queue in list format."""
    themes = []
    if flagged:
        # TODO (ngoke): rename to STATUS_FLAGGED.
        themes = Addon.objects.filter(status=amo.STATUS_REVIEW_PENDING,
                                      type=amo.ADDON_PERSONA,
                                      persona__isnull=False)
    elif rereview:
        themes = [
            rqt.theme.addon for rqt in
            RereviewQueueTheme.objects.select_related('theme__addon')]
    else:
        themes = Addon.objects.filter(status=amo.STATUS_PENDING,
                                      type=amo.ADDON_PERSONA,
                                      persona__isnull=False)

    search_form = forms.ThemeSearchForm(request.GET)
    per_page = request.GET.get('per_page', QUEUE_PER_PAGE)
    pager = paginate(request, themes, per_page)

    return render(request, 'reviewers/themes/queue_list.html', context(
        **{'addons': pager.object_list,
           'flagged': flagged,
           'pager': pager,
           'rereview': rereview,
           'theme_search_form': search_form,
           'statuses': dict((k, six.text_type(v)) for k, v in
                            amo.STATUS_CHOICES_API.items()),
           'tab': ('rereview_themes' if rereview else
                   'flagged_themes' if flagged else 'pending_themes')}))


def _themes_queue(request, flagged=False, rereview=False):
    """Themes queue in interactive format."""
    themes = _get_themes(request, request.user, flagged=flagged,
                         rereview=rereview)

    ThemeReviewFormset = formset_factory(forms.ThemeReviewForm)
    formset = ThemeReviewFormset(
        initial=[{'theme': _rereview_to_theme(rereview, theme).id} for theme
                 in themes])

    return render(request, 'reviewers/themes/queue.html', context(
        **{'actions': get_actions_json(),
           'formset': formset,
           'flagged': flagged,
           'reject_reasons': amo.THEME_REJECT_REASONS,
           'rereview': rereview,
           'reviewable': True,
           'theme_formsets': list(zip(themes, formset)),
           'theme_count': len(themes),
           'tab': (
               'flagged' if flagged else
               'rereview' if rereview else 'pending')}))


def _get_themes(request, reviewer, flagged=False, rereview=False):
    """Check out themes.

    :param flagged: Flagged themes (amo.STATUS_REVIEW_PENDING)
    :param rereview: Re-uploaded themes (RereviewQueueTheme)

    """
    num = 0
    themes = []
    locks = []

    status = (amo.STATUS_REVIEW_PENDING if flagged else
              amo.STATUS_PUBLIC if rereview else amo.STATUS_PENDING)

    if rereview:
        # Rereview themes.
        num, themes, locks = _get_rereview_themes(reviewer)
    else:
        # Pending and flagged themes.
        locks = ThemeLock.objects.filter(
            reviewer=reviewer, theme__addon__status=status)
        num, themes = _calc_num_themes_checkout(locks)
        if themes:
            return themes
        themes = Persona.objects.filter(
            addon__status=status, themelock=None)

    # Don't allow self-reviews.
    if (not settings.ALLOW_SELF_REVIEWS and
            not acl.action_allowed(request, amo.permissions.REVIEWS_ADMIN)):
        if rereview:
            themes = themes.exclude(theme__addon__addonuser__user=reviewer)
        else:
            themes = themes.exclude(addon__addonuser__user=reviewer)

    # Check out themes by setting lock.
    themes = list(themes)[:num]
    expiry = get_updated_expiry()
    for theme in themes:
        ThemeLock.objects.create(theme=_rereview_to_theme(rereview, theme),
                                 reviewer=reviewer, expiry=expiry)

    # Empty pool? Go look for some expired locks.
    if not themes:
        expired_locks = ThemeLock.objects.filter(
            expiry__lte=datetime.datetime.now(),
            theme__addon__status=status)[:amo.THEME_INITIAL_LOCKS]
        # Steal expired locks.
        for lock in expired_locks:
            lock.reviewer = reviewer
            lock.expiry = expiry
            lock.save()
        if expired_locks:
            locks = expired_locks

    if rereview:
        return (RereviewQueueTheme.objects
                .filter(theme__themelock__reviewer=reviewer)
                .exclude(theme__addon__status=amo.STATUS_REJECTED))

    # New theme locks may have been created, grab all reviewer's themes again.
    return [lock.theme for lock in locks]


@json_view
@personas_reviewer_required
def themes_search(request):
    search_form = forms.ThemeSearchForm(request.GET)
    if search_form.is_valid():
        q = search_form.cleaned_data['q']
        rereview = search_form.cleaned_data['queue_type'] == 'rereview'
        flagged = search_form.cleaned_data['queue_type'] == 'flagged'

        # ES query on name.
        themes = Addon.search().filter(type=amo.ADDON_PERSONA)
        if rereview:
            themes = themes.filter(has_theme_rereview=True)
        else:
            themes = themes.filter(status=(amo.STATUS_REVIEW_PENDING if flagged
                                           else amo.STATUS_PENDING),
                                   has_theme_rereview=False)
        themes = themes.filter_query_string(q)[:100]

        now = datetime.datetime.now()
        reviewers = []
        for theme in themes:
            try:
                themelock = theme.persona.themelock
                if themelock.expiry > now:
                    reviewers.append(themelock.reviewer.email)
                else:
                    reviewers.append('')
            except ObjectDoesNotExist:
                reviewers.append('')

        themes = list(themes.values_dict('name', 'slug', 'status'))

        for theme, reviewer in zip(themes, reviewers):
            # Collapse single value fields from a list.
            theme['id'] = theme['id']
            theme['slug'] = theme['slug']
            theme['status'] = theme['status']
            # Dehydrate.
            theme['reviewer'] = reviewer

        return {'objects': themes, 'meta': {'total_count': len(themes)}}


@personas_reviewer_required
def themes_queue(request):
    # By default, redirect back to the queue after a commit.
    request.session['theme_redirect_url'] = reverse(
        'reviewers.themes.queue_themes')