from django.contrib.auth.models import User, Group
from django.contrib.auth import authenticate
from django.contrib.sites.models import Site
from django.conf import settings
from django.template import RequestContext, Context, loader
from django.utils.encoding import force_unicode, smart_str
from django.core import validators
from django import newforms
from pycon.tags.forms import MultiSelectComboMultiWordTagField
from pycon.restructuredtext.forms import ReSTField
from pycon.core import safe_utf8_encode
from itertools import chain
from models import *
from changelog import log_proposal, log_action, CHANGE
import types

EXCLUDE_PROPOSAL_FIELDS = ['submitter',
                           'attachedfile',
                           'coauthors',
                           'published',
                           'status',
                           'submitted',
                           'updated',
                           'reviewers',
                           'optout',
                           'published_description']

DEFAULT_PROPOSAL_TAGS = ['advocacy',
                         'agile',
                         'animation',
                         'biology',
                         'case study',
                         'community',
                         'concurrency',
                         'core',
                         'databases',
                         'django',
                         'documentation',
                         'education',
                         'games',
                         'gui',
                         'handheld',
                         'ide',
                         'jython',
                         'language',
                         'legal',
                         'network',
                         'packaging',
                         'panel',
                         'parsing',
                         'science',
                         'security',
                         'testing',
                         'turbogears',
                         'twisted',
                         'university',
                         'web',
                         'zope']

DEFAULT_PROPOSAL_TAGS = zip(DEFAULT_PROPOSAL_TAGS, DEFAULT_PROPOSAL_TAGS)
DEFAULT_DURATIONS = [(30, '30min'),
                     (45, '45min')]


class DurationSelection(newforms.Select):
    def __init__(self, attrs=None, choices=(), empty_label=u"---------"):
        self.empty_label = empty_label
        super(DurationSelection, self).__init__(attrs, choices)

    def render(self, name, value, attrs=None, choices=(), **kwdargs):
        empty_choice = ()
        # kwdargs is needed because it is the only way to determine if an
        # override is provided or not.
        if 'empty_label' in kwdargs:
            if kwdargs['empty_label'] is not None:
                empty_choice = ((u'', kwdargs['empty_label']),)
        elif self.empty_label is not None:
            empty_choice = ((u'', self.empty_label),)
        base_choices = self.choices
        self.choices = chain(empty_choice, base_choices)
        if (value is not None and
            value not in dict(chain(empty_choice, base_choices, choices))):
            choices = chain(choices, [(value, unicode(value) + u'min')])
        result = super(DurationSelection, self).render(name, value,
                                                       attrs, choices)
        self.choices = base_choices
        return result

def filter_proposal_fields(f):
    if f.name in EXCLUDE_PROPOSAL_FIELDS:
        return None
    if f.name == 'title':
        ff = f.formfield()
        ff.widget.attrs['size'] = u'85'
        return ff
    if f.name == 'categories':
        ff = f.formfield()
        return MultiSelectComboMultiWordTagField(
            Proposal, choices=DEFAULT_PROPOSAL_TAGS, required=ff.required,
            select_attrs={'class': u"vSelectMultipleField", 'size': u'15' },
            text_attrs={'size': u'55'}, max_tags=10,
            max_length=ff.max_length, min_length=ff.min_length)
    if f.name == 'summary':
        ff = f.formfield()
        ff.widget.attrs['cols'] = 85
        return ff
    if f.name == 'description':
        return ReSTField(include_error=True, require_title=False,
                         rows=30, cols=85, aclass="monospace")
    if f.name == 'duration':
        return newforms.ChoiceField(choices=DEFAULT_DURATIONS,
                                    widget=DurationSelection)
    return f.formfield()

_ProposalForm = newforms.form_for_model(
    Proposal, formfield_callback=filter_proposal_fields)


def get_reviewers(num=1, exclude=[]):
    reviewers = Group.objects.get(name='Reviewers').user_set.exclude(
        pk__in=exclude).extra(
        select={
            'count': 'SELECT COUNT(*) FROM propmgr_proposal_reviewers '
                     'WHERE propmgr_proposal_reviewers.user_id = auth_user.id'
        },
        ).distinct().order_by('count')
    if not reviewers.count(): return []


class ProposalForm(_ProposalForm):
    def __init__(self, request, proposal=None, post=None, *args, **kwdargs):
        self.proposal = proposal
        self.user = request.user
        self.request = request
        if post is None and request.POST:
            post = request.POST
        initial = {}
        if proposal is not None:
            initial.update(vars(proposal))
            initial['categories'] = initial['_categories_cache']
            del initial['_categories_cache'] ## tag field is messed up.
        if 'initial' in kwdargs:
            initial.update(kwdargs['initial'])
        kwdargs['initial'] = initial
        super(ProposalForm, self).__init__(post, *args, **kwdargs)
    def clean_title(self):
        value = self.cleaned_data['title']
        if self.proposal is not None:
            if value == self.proposal.title: return value
        try: Proposal.objects.get(title__exact=value)
        except Proposal.DoesNotExist: return value
        raise newforms.ValidationError('There is already a ' +
            Proposal._meta.verbose_name + ' with a title of "' + value +'".')
    def clean_duration(self):
        value = self.cleaned_data['duration']
        try:
            value = int(value)
        except e:
            raise newforms.ValidationError('Not a valid duration: '  + str(e))
        return value
    def save(self):
        if self.proposal is not None:
            log_change = log_proposal(self.user, self.cleaned_data,
                                      self.proposal)
            res = newforms.save_instance(self, self.proposal,
                                         fail_message='edit')
            log_change()
            return res
        ## new proposal
        prop = super(ProposalForm, self).save(commit=False)
        prop.submitter = self.user
        ### get some new reviewers!!!
        reviewers = []
        try:
            reviewers = Group.objects.get(name='Reviewers').user_set.exclude(
                    pk=prop.submitter_id).extra(select={'count':
                        'SELECT COUNT(*) '
                        'FROM propmgr_proposal_reviewers '
                        'WHERE propmgr_proposal_reviewers.user_id = auth_user.id'},
                    ).distinct().order_by('count')[:3]
            prop.save()
            prop.reviewers = reviewers
            ## moved to model save
            ##user.groups.add(Group.objects.get(name='Submitters'))
        except:
            if settings.DEBUG: raise
        prop.save()
        log_proposal(self.user, prop)
        emails = []
        t = loader.get_template("propmgr/email/submit_proposal.txt")
        c = {
            'mailto': prop.submitter,
            'email': prop.submitter.email,
            'proposal': prop
        }
        message = t.render(RequestContext(self.request, c))
        emails.append({
            'subject': settings.CONFERENCE_NAME + ' Talk Proposal Submission',
            'body': message,
            'from_email': settings.PROPMGR_FROM_EMAIL,
            'replyto_email': settings.PROPMGR_REPLYTO_EMAIL,
            'to': [prop.submitter.email]})
        t = loader.get_template("propmgr/email/review_proposal.txt")
        for reviewer in reviewers:
            c = {
                'mailto': reviewer,
                'email': reviewer.email,
                'proposal': prop
            }
            message = t.render(RequestContext(self.request, c))
            emails.append({
                'subject': settings.CONFERENCE_NAME +
                           ' New Talk Proposal Submission for Review',
                'body': message,
                'from_email': settings.PROPMGR_FROM_EMAIL,
                'replyto_email': settings.PROPMGR_REPLYTO_EMAIL,
                'to': [reviewer.email]})
        from pycon.core import mail
        #print emails # because dev does not have sendmail enabled.
        mail.send_mass_mail(emails)
        return prop

class NullSelection(newforms.Select):
    def __init__(self, attrs=None, choices=(), empty_label=u"---------"):
        self.empty_label = empty_label
        super(NullSelection, self).__init__(attrs, choices)

    def render(self, name, value, attrs=None, choices=(), **kwdargs):
        empty_choice = ()
        # kwdargs is needed because it is the only way to determine if an
        # override is provided or not.
        if 'empty_label' in kwdargs:
            if kwdargs['empty_label'] is not None:
                empty_choice = ((u'', kwdargs['empty_label']),)
        elif self.empty_label is not None:
            empty_choice = ((u'', self.empty_label),)
        base_choices = self.choices
        self.choices = chain(empty_choice, base_choices)
        result = super(NullSelection, self).render(name, value,
                                                   attrs, choices)
        self.choices = base_choices
        return result

class StatusEntryForm(newforms.Form):
    status = newforms.ChoiceField(required=False, choices=(),
                                  widget=NullSelection)
    def __init__(self, proposal, user, editable=None, *args, **kwdargs):
        if editable is None:
            editable = (not proposal.published and
                        proposal.status in ['A', 'D', 'R'])
        self.editable = editable
        self.proposal = proposal
        self.user = user
        super(StatusEntryForm, self).__init__(*args, **kwdargs)
        self.fields['status'].choices=tuple((key, val)
            for key, val in PROPOSAL_STATUS_CHOICES if key != proposal.status)
        proposal.form = self
    def save(self):
        if not self.editable: return False
        new_status = self['status'].data
        if not new_status or new_status == self.proposal.status:
            return False
        self.proposal.status = new_status
        log_action(self.user, self.proposal, CHANGE, '* Status Change\n')
        self.proposal.save()
        return True
    def __unicode__(self):
        if self.editable:
            return unicode(self['status'])
        if self.proposal.published: return u'Published'
        return u'Not Editable'

def proposal_status_form_filter(query, user, data=None):
    return [ StatusEntryForm(p, user, prefix=u'%d'%p.id, data=data).proposal
             for p in query ]

class EMailForm(newforms.Form):
    from_val = getattr(settings, 'PROPMGR_FROM_EMAIL',
                       getattr(settings, 'DEFAULT_FROM_EMAIL',
                               getattr(settings, 'SERVER_EMAIL')))
    to      = newforms.ChoiceField(required=True, choices=())
    metoo   = newforms.BooleanField(label='Send Me A Copy', initial=True)
    reply   = newforms.CharField(required=True, label='Reply-To',
                    initial=getattr(settings, 'PROPMGR_REPLYTO_EMAIL', ''))
    subject = newforms.CharField(required=True,
                    initial=getattr(settings, 'CONFERENCE_NAME', ''))
    content = newforms.CharField(required=True, widget=newforms.Textarea)


    def __init__(self, request, post=None, *args, **kwdargs):
        self.user = request.user
        self.request = request
        if post is None and request.POST:
            post = request.POST
        initial = {
            'content': u'''


    -- %s
    -- The %s Program Committee
            ''' % (unicode(self.user), getattr(settings,'CONFERENCE_NAME', '')),
        }
        if 'initial' in kwdargs:
            initial.update(kwdargs['initial'])
        kwdargs['initial'] = initial
        super(EMailForm, self).__init__(post, *args, **kwdargs)
        groups = map(unicode, Group.objects.all())
        self.fields['to'].choices = zip(groups, groups)
        self.fields['content'].widget.attrs['rows'] = 30
        self.fields['content'].widget.attrs['cols'] = 85

    def send(self):
        gname = self.cleaned_data['to']
        reply = self.cleaned_data['reply']
        subject = self.cleaned_data['subject']
        body = self.cleaned_data['content']
        metoo = self.cleaned_data['metoo']

        users = list(Group.objects.get(name=gname).user_set.all())
        if metoo:
            if self.user.id not in [ u.id for u in users ]:
                users.append(self.user)

        messages = [
            {'subject': smart_str(subject),
             'body': body,
             'from_email': self.from_val,
             'replyto_email': reply,
             'to': [user.email]}
            for user in users
        ]
        from pycon.core import mail
        #print emails # because dev does not have sendmail enabled.
        mail.send_mass_mail(messages)
        return len(users)
