"""proposal system views
"""
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponseRedirect
from django.http import HttpResponse, Http404
from django.shortcuts import *
from django.template import loader
from django.db.models import Q
from django.template import RequestContext
from django.views.generic.list_detail import object_list, object_detail
from django.views.generic.create_update import create_object, update_object
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Group
from django import forms
from itertools import groupby
from pycon.features.decorators import *
from pycon.core import get_object_relations_or_404
from models import Proposal, PROPOSAL_LEVEL_CHOICES
from forms import *

def catchall(request, url, redirect=""):
    redirect_to = request.path[:len(url)]
    if redirect:
        if redirect_to[-1] != "/":
            redirect_to += "/"
        redirect_to += redirect
    if redirect_to[-1] != "/":
        redirect_to += "/"
    return HttpResponseRedirect(redirect_to)

def is_reviewer(request, *args, **kwdargs):
    return request.user.has_perm('propmgr.can_view_all_reviews')

@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def reviewers_list(request, **extra_context):
    if not request.user.can_view_proposal_stats:
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view reviewers',
                                       'title': 'Proposal Reviewers'},
                              context_instance=RequestContext(request))
    page_cache_name = '_'.join([str(request.user.id),
                                str(bool(int(request.session.get('minimal_view', False)))),
                                'reviewers_list'])
    page_cache = cache.get(page_cache_name)
    if page_cache: return HttpResponse(page_cache)
    
    extra_context['title'] = 'Proposal Reviewers'
    extra_context['proposal_count'] = Proposal.objects.count()
    extra_context['reviewers'] = Group.objects.get(
        name='Reviewers').user_set.order_by('first_name', 'last_name')
    page_cache = loader.render_to_string('propmgr/reviewers.html', extra_context,
                                         context_instance=RequestContext(request))
    cache.set(page_cache_name, page_cache, 120)
    return HttpResponse(page_cache)

@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def authors_list(request, **extra_context):
    if not request.user.can_view_proposal_stats:
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view authors',
                                       'title': 'Proposal Authors'},
                              context_instance=RequestContext(request))
    page_cache_name = '_'.join([str(request.user.id),
                                str(bool(int(request.session.get('minimal_view', False)))),
                                'votes_list'])
    page_cache = cache.get(page_cache_name)
    if page_cache: return HttpResponse(page_cache)

    extra_context['title'] = 'Proposal Authors'
    extra_context['proposal_count'] = Proposal.objects.count()
    extra_context['sub_count'] = Group.objects.get(
        name='Submitters').user_set.count()
    extra_context['authors'] = Group.objects.get(
        name='Authors').user_set.order_by('first_name', 'last_name')
    page_cache = loader.render_to_string('propmgr/submitters.html', extra_context,
                                         context_instance=RequestContext(request))
    cache.set(page_cache_name, page_cache, 120)    
    return HttpResponse(page_cache)

@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def votes_list(request, **extra_context):
    if not request.user.can_view_proposal_stats and not request.user.has_perm('propmgr.can_view_all_reviews'):
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view votes',
                                       'title': 'Proposal Votes'},
                              context_instance=RequestContext(request))
    
    page_cache_name = '_'.join([str(request.user.id),
                                str(bool(int(request.session.get('minimal_view', False)))),
                                'votes_list'])
    page_cache = cache.get(page_cache_name)
    if page_cache: return HttpResponse(page_cache)

    extra_context['prop_count'] = Proposal.objects.count()
    extra_context['rev_count'] = Group.objects.get(name='Reviewers').user_set.count()
    extra_context['vote_count'] = Review.objects.count()
    extra_context['title'] = 'Proposal Votes'
    proposals = Proposal.objects.select_related()
    ranked = {}
    for name, propiter in groupby(proposals, Proposal.get_review_rank):
        data = ranked.setdefault(name, list())
        data.extend(propiter)
    ranked['needs_champion'] = Proposal.needs_champion_set().select_related()
    extra_context['ranked_proposals'] = ranked
    page_cache = loader.render_to_string('propmgr/votes.html', extra_context,
                                         context_instance=RequestContext(request))
    cache.set(page_cache_name, page_cache, 30)    
    return HttpResponse(page_cache)

@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def status_list(request, **extra_context):
    if not request.user.can_view_proposal_stats and not request.user.has_perm('propmgr.can_view_all_reviews'):
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view status',
                                       'title': "Proposal Status'"},
                              context_instance=RequestContext(request))
    
    page_cache_name = '_'.join([str(request.user.id),
                                str(bool(int(request.session.get('minimal_view', False)))),
                                'status_list'])
    page_cache = cache.get(page_cache_name)
    if page_cache: return HttpResponse(page_cache)

    extra_context['accepted'] = Proposal.objects.filter(status='A')
    extra_context['declined'] = Proposal.objects.filter(status='D')
    extra_context['reviewable'] = Proposal.objects.filter(status='R')
    extra_context['other'] = Proposal.objects.exclude(status__in=['A', 'D', 'R'])
    extra_context['title'] = "Proposal Status'"
    page_cache = loader.render_to_string('propmgr/proposal_status.html', extra_context,
                                         context_instance=RequestContext(request))
    cache.set(page_cache_name, page_cache, 30)    
    return HttpResponse(page_cache)

@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def users_proposal_list(request, **extra_context):
    if request.user.can_view_proposal_stats:
        extra_context['prop_count'] = Proposal.objects.count()
        extra_context['sub_count'] = Group.objects.get(name='Submitters').user_set.count()
        extra_context['auth_count'] = Group.objects.get(name='Authors').user_set.count()
        extra_context['rev_count'] = Group.objects.get(name='Reviewers').user_set.count()
        extra_context['vote_count'] = Review.objects.count()
    return render_to_response('propmgr/mypycon.html', extra_context,
                              context_instance=RequestContext(request))

    
@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def index(request, *args, **kwdargs):
    if not request.user.can_view_proposal_listing:
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view proposals',
                                       'title': 'All Proposals'},
                              context_instance=RequestContext(request))
    context = kwdargs.setdefault('extra_context', {})
    myargs = {'template_object_name' : 'proposal',
              'allow_empty': True}
    myargs.update(kwdargs)
    query = Proposal.objects
    search = []
    if 'search' in request.GET:
        search = request.GET['search'].split()
        if len(search) == 2 and search[0] == '--Search' and search[1] == 'Proposals--':
            search = []
    if search:
        qset = []
        for term in search:
            qset.append(Q(title__icontains = term)|
                        Q(category__icontains = term) |
                        Q(summary__icontains = term) |
                        Q(description__icontains = term))
        query = query.filter(*qset).distinct()
    context['search'] = search
    context.setdefault('title', "All Proposals")
    return object_list(request, query.select_related(),
                       *args, **myargs)

@login_required
@feature_required("ViewProposal", exception_func=is_reviewer,
                  auto_create_feat=True)
def detail(request, *args, **kwdargs):
    proposal = get_object_relations_or_404(Proposal, pk=kwdargs.get('object_id'))
    if not request.user.can_view_proposal(proposal):
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'view this proposal',
                                       'title': 'View Proposal'},
                              context_instance=RequestContext(request))
    context = kwdargs.setdefault('extra_context', dict())
    context['prop_and_user'] = (proposal, request.user)
    kwdargs.setdefault('queryset', Proposal.objects.all())
    kwdargs.setdefault('template_object_name', 'proposal')
    return object_detail(request, *args, **kwdargs)

def _proposal_submit(request, manipulator, propid, title, new=False, redirect_to=None, prop=None):
    extra_context = {'preview': False, 'title': title}
    if request.POST:
        new_data = request.POST.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors and ('__submit' in new_data):
            proposal = manipulator.save(new_data)
            if not redirect_to:
                redirect_to = '../%s/' % str(proposal.id)
            return HttpResponseRedirect(redirect_to)
        #if '__preview' in new_data:
        extra_context['propid'] = propid
        extra_context['preview'] = True
        extra_context['proposal'] = None
        if not new:
            extra_context['proposal'] = prop
    else:
        errors = {}
        new_data = manipulator.flatten_data()
    extra_context['level_choices'] = PROPOSAL_LEVEL_CHOICES
    extra_context['is_new'] = new
    extra_context['has_errors'] = bool(errors)
    extra_context['redirect_to']= '../' ## Cancel button
    extra_context['form'] = forms.FormWrapper(manipulator, new_data, errors)
    return render_to_response('propmgr/proposal_submit.html', extra_context,
                              context_instance=RequestContext(request))

@feature_required("SubmitProposal", auto_create_feat=True)
def submit_new_proposal(request):
    if not request.user.can_add_proposal:
        return render_to_response('propmgr/lack_permissions.html',
                                      {'operation': 'submit a new proposal',
                                       'title': 'Submit New Talk Proposal'},
                              context_instance=RequestContext(request))
    manipulator = NewProposalForm(request)
    return _proposal_submit(request, manipulator, '##',
                            'Submit New Talk Proposal', True, None)

@login_required
@feature_required("EditProposal", auto_create_feat=True)
def edit_proposal(request, id, redirect_to=".."):
    prop = get_object_relations_or_404(Proposal,pk=id)
    if not request.user.can_edit_proposal(prop):
        return render_to_response('propmgr/lack_permissions.html',
                                  {'title': 'Edit Proposal',
                                   'operation': 'edit this talk proposal'},
                          context_instance=RequestContext(request))
    manipulator = EditProposalForm(prop, request.user)
    return _proposal_submit(request, manipulator, id,
                            'Edit Talk Proposal', False, redirect_to, prop)

@login_required
@feature_required("ReviewProposal", auto_create_feat=True)
def reviewer_opt_out(request, pid):
    prop = get_object_relations_or_404(Proposal,pk=pid)
    if not request.user.can_add_review(prop):
        return render_to_response('propmgr/lack_permissions.html',
                                  {'title': 'Reviewer Opt Out',
                                   'operation':
                                        'opt-out of reviewing this proposal'},
                          context_instance=RequestContext(request))
    reviewer = None
    if prop.reviewers.count() <= 3:
        conflict = [ auth.id for auth in prop.coauthors.all() ]
        conflict.append( prop.submitter.id )
        conflict.extend( rev.id for rev in prop.reviewers.all() )
        try:
            reviewer = Group.objects.get(name='Reviewers').user_set.exclude(
                id__in=conflict).order_by('?')[0]
        except IndexError: pass
    prop.reviewers.remove(request.user)
    if reviewer is not None:
        prop.reviewers.add(reviewer)
        t = loader.get_template("propmgr/email/review_proposal.txt")
        c = {
            'mailto': reviewer,
            'email': reviewer.email,
            'proposal': prop
        }
        message = t.render(RequestContext(request, c))
        from pycon.core import mail
        #print message # because dev does not have sendmail enabled.
        mail.send_mail('PyCon 2007 Reassigned Talk Proposal Submission for Review',
                       message, settings.PROPMGR_EMAIL,
                       [reviewer.email], fail_silently=True)

    prop.save()
    return HttpResponseRedirect('../')

@login_required
@feature_required("ReviewProposal", auto_create_feat=True)
def add_or_edit_review(request, pid, rid=None):
    prop = get_object_or_404(Proposal,pk=pid)
    review=None
    if rid is None:
        title = 'Review Proposal'
        if not request.user.can_add_review(prop):
            return render_to_response('propmgr/lack_permissions.html',
                                  {'title': title,
                                   'operation': 'review this talk proposal'},
                          context_instance=RequestContext(request))
        manipulator = NewReviewForm(pid, request.user)
        redirect_to = '../'
    else:
        title = 'Edit Review'
        review = get_object_or_404(Review,pk=rid)
        if not request.user.can_edit_review(review):
            return render_to_response('propmgr/lack_permissions.html',
                                  {'title': title,
                                   'operation': 'edit this talk proposal review'},
                           context_instance=RequestContext(request))
        manipulator = EditReviewForm(rid)
        redirect_to = "../../"
    
    extra_context = {'preview': False, 'title': title,
                     'redirect_to': redirect_to,
                     'review': review}
    if request.POST:
        new_data = request.POST.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors and '__submit' in new_data:
            manipulator.save(new_data)
            return HttpResponseRedirect(redirect_to)
        #if '__preview' in new_data:
        extra_context['proposal'] = prop
        extra_context['preview'] = True
    else:
        errors = {}
        new_data = manipulator.flatten_data()
    extra_context['has_errors'] = bool(errors)
    extra_context['form'] = forms.FormWrapper(manipulator, new_data, errors)
    return render_to_response('propmgr/review_edit.html', extra_context,
                              context_instance=RequestContext(request))

@login_required
@feature_required("EditProposal", auto_create_feat=True)
def edit_authors(request, pid):
    prop = get_object_relations_or_404(Proposal,pk=pid)
    if not request.user.can_edit_coauthors(prop):
        return render_to_response('propmgr/lack_permissions.html',
                              {'title': 'Edit Co-Authors',
                               'operation': 'edit co-authors for this talk proposal'},
                      context_instance=RequestContext(request))
    manipulator = EditCoAuthorsForm(prop)
    
    extra_context = {'title': 'Edit Co-Authors', 'proposal': prop}
    if request.POST:
        new_data = request.POST.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors:
            manipulator.save(new_data)
            return HttpResponseRedirect('../')
    else:
        errors = {}
        new_data = manipulator.flatten_data()
    extra_context['form'] = forms.FormWrapper(manipulator, new_data, errors)
    return render_to_response('propmgr/authors_edit.html', extra_context,
                              context_instance=RequestContext(request))


@login_required
@feature_required("CommentOnProposal", auto_create_feat=True)
def add_comment(request, pid):
    prop = get_object_or_404(Proposal,pk=pid)
    if not request.user.can_add_comment(prop):
        return render_to_response('propmgr/lack_permissions.html',
                              {'title': 'Add Comment',
                               'operation': 'add a comment to this talk proposal'},
                      context_instance=RequestContext(request))
    manipulator = NewCommentForm(pid, request)
    
    extra_context = {'preview': False, 'title': 'Add Comment',
                     'redirect_to': '../'}
    if request.POST:
        new_data = request.POST.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors and '__submit' in new_data:
            manipulator.save(new_data)
            return HttpResponseRedirect('../')
        #if '__preview' in new_data:
        extra_context['proposal'] = prop
        extra_context['preview'] = True
    else:
        errors = {}
        new_data = manipulator.flatten_data()
    extra_context['prop_and_user'] = (prop, request.user)
    extra_context['has_errors'] = bool(errors)
    extra_context['form'] = forms.FormWrapper(manipulator, new_data, errors)
    return render_to_response('propmgr/comment_add.html', extra_context,
                              context_instance=RequestContext(request))


@login_required
@feature_required("EditProposal", auto_create_feat=True)
def attach_file(request, pid):
    prop = get_object_or_404(Proposal,pk=pid)
    if not request.user.can_edit_proposal(prop):
        return render_to_response('propmgr/lack_permissions.html',
                              {'title': 'Upload File',
                               'operation': 'attach a file to this talk proposal'},
                      context_instance=RequestContext(request))
    manipulator = AttachFileForm(pid, request.user)
    extra_context = {'title': 'Upload File'}
    if request.POST:
        new_data = request.POST.copy()
        new_data.update(request.FILES)
        errors = manipulator.get_validation_errors(new_data)
        if not errors:
            manipulator.save(new_data)
            return HttpResponseRedirect('../')
    else:
        errors = {}
        new_data = manipulator.flatten_data()
        
    extra_context['form'] = forms.FormWrapper(manipulator, new_data, errors)
    return render_to_response('propmgr/attach_file.html', extra_context,
                              context_instance=RequestContext(request))


def _get_filter_listing(proposals):
    cats = cache.get('propmgr_accepted_published_talk_categories', None)
    if cats is not None: return cats
    cats = sorted(list(set(cat.strip() for prop in proposals for cat in prop.category.split(','))))
    cache.set('propmgr_accepted_published_talk_categories', cats, 60*60*24)
    return cats
    
@feature_required("ViewAcceptedProposals",
                  auto_create_feat=True)
def accepted_talks(request):
    filter = request.GET.get('filter', None)
    cache_name = ''
    if not request.user.is_anonymous(): cache_name = repr(request.user.id) + '_'
    cache_name += 'propmgr_accepted_talks_' + str(bool(int(request.session.get('minimal_view', False))))
    if filter is not None: cache_name += '_' + filter.replace(' ', '_')
    page_cache = cache.get(cache_name, None)
    if page_cache is not None: return HttpResponse(page_cache)

    query = Proposal.objects.filter(status='A', published=True)
    cats = _get_filter_listing(query)
    if filter not in cats: filter=None
    if filter is not None:
        # we need to be careful here to match 'rest,' but not 'restructuredtext,'?
        query = query.filter(Q(category__contains = filter+',') |
                             Q(category__endswith = filter))
    
    extra_context = {}
    title = "Talks"
    if filter is not None: title += ' (' + filter +')'
    extra_context['filter']     = filter
    extra_context['title']      = title
    extra_context['proposals']  = query
    extra_context['categories'] = cats
    page_cache = loader.render_to_string('propmgr/accepted_talks.html', extra_context,
                                         context_instance=RequestContext(request))
    cache.set(cache_name, page_cache, 60*60)    
    return HttpResponse(page_cache)

