# Copyright The IETF Trust 2007, All Rights Reserved
import datetime
from email.utils import parseaddr

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.core.validators import email_re
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils import simplejson
from django.views.generic.list_detail import object_list, object_detail

from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaison,
                                    can_add_incoming_liaison, LIAISON_EDIT_GROUPS,
                                    is_ietfchair, is_iabchair, is_iab_executive_director,
                                    can_edit_liaison, is_secretariat)
from ietf.liaisons.decorators import can_submit_liaison
from ietf.liaisons.forms import liaison_form_factory
from ietf.liaisons.models import LiaisonDetail, OutgoingLiaisonApproval
from ietf.liaisons.utils import IETFHM

if settings.USE_DB_REDESIGN_PROXY_CLASSES:
    from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail


@can_submit_liaison
def add_liaison(request, liaison=None):
    if request.method == 'POST':
        form = liaison_form_factory(request, data=request.POST.copy(),
                                    files = request.FILES, liaison=liaison)
        if form.is_valid():
            liaison = form.save()
            if request.POST.get('send', None):
                if not settings.DEBUG:
                    liaison.send_by_email()
                else:
                    return _fake_email_view(request, liaison)
            return HttpResponseRedirect(reverse('liaison_list'))
    else:
        form = liaison_form_factory(request, liaison=liaison)

    return render_to_response(
        'liaisons/liaisondetail_edit.html',
        {'form': form,
         'liaison': liaison},
        context_instance=RequestContext(request),
    )


@can_submit_liaison
def get_info(request):
    person = get_person_for_user(request.user)

    to_entity_id = request.GET.get('to_entity_id', None)
    from_entity_id = request.GET.get('from_entity_id', None)

    result = {'poc': [], 'cc': [], 'needs_approval': False, 'post_only': False, 'full_list': []}

    to_error = 'Invalid TO entity id'
    if to_entity_id:
        to_entity = IETFHM.get_entity_by_key(to_entity_id)
        if to_entity:
            to_error = ''

    from_error = 'Invalid FROM entity id'
    if from_entity_id:
        from_entity = IETFHM.get_entity_by_key(from_entity_id)
        if from_entity:
            from_error = ''

    if to_error or from_error:
        result.update({'error': '\n'.join([to_error, from_error])})
    else:
        result.update({'error': False,
                       'cc': [i.email() for i in to_entity.get_cc(person=person)] +\
                             [i.email() for i in from_entity.get_from_cc(person=person)],
                       'poc': [i.email() for i in to_entity.get_poc()],
                       'needs_approval': from_entity.needs_approval(person=person),
                       'post_only': from_entity.post_only(person=person, user=request.user)})
        if is_secretariat(request.user):
            full_list = [(i.pk, i.email()) for i in set(from_entity.full_user_list())]
            full_list.sort(lambda x,y: cmp(x[1], y[1]))
            full_list = [(person.pk, person.email())] + full_list
            result.update({'full_list': full_list})

    json_result = simplejson.dumps(result)
    return HttpResponse(json_result, mimetype='text/javascript')


def _fake_email_view(request, liaison):
    mail = liaison.send_by_email(fake=True)
    return render_to_response('liaisons/liaison_mail_detail.html',
                              {'mail': mail,
                               'message': mail.message(),
                               'liaison': liaison},
                              context_instance=RequestContext(request))

if settings.USE_DB_REDESIGN_PROXY_CLASSES:
    def approvable_liaison_statements(group_codes):
        # this is a bit complicated because IETFHM encodes the
        # groups, it should just give us a list of ids or acronyms
        group_acronyms = []
        group_ids = []
        for x in group_codes:
            if "_" in x:
                group_ids.append(x.split("_")[1])
            else:
                group_acronyms.append(x)

        return LiaisonDetail.objects.filter(approved=None).filter(Q(from_group__acronym__in=group_acronyms) | Q (from_group__pk__in=group_ids))

def liaison_list(request):
    user = request.user
    can_send_outgoing = can_add_outgoing_liaison(user)
    can_send_incoming = can_add_incoming_liaison(user)
    can_approve = False
    can_edit = False

    person = get_person_for_user(request.user)
    if person:
        approval_codes = IETFHM.get_all_can_approve_codes(person)
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            can_approve = approvable_liaison_statements(approval_codes).count()
        else:
            can_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).count()

    order = request.GET.get('order_by', 'submitted_date')
    plain_order = order
    reverse_order = order.startswith('-')
    if reverse_order:
        plain_order = order[1:]
    if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'):
        order = 'submitted_date'
        reverse_order = True
        plain_order = 'submitted_date'
    elif plain_order in ('submitted_date', 'deadline_date'):
        # Reverse order for date fields, humans find it more natural
        if reverse_order:
            order = plain_order
        else:
            order = '-%s' % plain_order
    if settings.USE_DB_REDESIGN_PROXY_CLASSES:
        public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order)
    else:
        public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order)

    return object_list(request, public_liaisons,
                       allow_empty=True,
                       template_name='liaisons/liaisondetail_list.html',
                       extra_context={'can_manage': can_approve or can_send_incoming or can_send_outgoing,
                                      'can_approve': can_approve,
                                      'can_edit': can_edit,
                                      'can_send_incoming': can_send_incoming,
                                      'can_send_outgoing': can_send_outgoing,
                                      plain_order: not reverse_order and '-' or None},
                      )


@can_submit_liaison
def liaison_approval_list(request):
    person = get_person_for_user(request.user)
    approval_codes = IETFHM.get_all_can_approve_codes(person)
    if settings.USE_DB_REDESIGN_PROXY_CLASSES:
        to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted")
    else:
        to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")

    return object_list(request, to_approve,
                       allow_empty=True,
                       template_name='liaisons/liaisondetail_approval_list.html',
                      )


@can_submit_liaison
def liaison_approval_detail(request, object_id):
    person = get_person_for_user(request.user)
    approval_codes = IETFHM.get_all_can_approve_codes(person)
    if settings.USE_DB_REDESIGN_PROXY_CLASSES:
        to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted")
    else:
        to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")

    if request.method=='POST' and request.POST.get('do_approval', False):
        try:
            liaison = to_approve.get(pk=object_id)
            if settings.USE_DB_REDESIGN_PROXY_CLASSES:
                liaison.approved = datetime.datetime.now()
                liaison.save()
            else:
                approval = liaison.approval
                if not approval:
                    approval = OutgoingLiaisonApproval.objects.create(approved=True, approval_date=datetime.datetime.now())
                    liaison.approval = approval
                    liaison.save()
                else:
                    approval.approved=True
                    approval.save()
            if not settings.DEBUG:
                liaison.send_by_email()
            else:
                return _fake_email_view(request, liaison)
        except LiaisonDetail.DoesNotExist:
            pass
        return HttpResponseRedirect(reverse('liaison_list'))
    return  object_detail(request,
                          to_approve,
                          object_id=object_id,
                          template_name='liaisons/liaisondetail_approval_detail.html',
                         )


def _can_take_care(liaison, user):
    if not liaison.deadline_date or liaison.action_taken:
        return False

    if user.is_authenticated():
        if is_secretariat(user):
            return True
        else:
            return _find_person_in_emails(liaison, get_person_for_user(user))
    return False
            

def _find_person_in_emails(liaison, person):
    if not person:
        return False
    emails = ','.join([ e for e in [liaison.cc1, liaison.cc2, liaison.to_email,
                       liaison.to_poc, liaison.submitter_email,
                       liaison.replyto, liaison.response_contact,
                       liaison.technical_contact] if e ])
    for email in emails.split(','):
        name, addr = parseaddr(email)
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            person.emailaddress_set = person.email_set
        if email_re.search(addr) and person.emailaddress_set.filter(address=addr):
            return True
        elif addr in ('chair@ietf.org', 'iesg@ietf.org') and is_ietfchair(person):
            return True
        elif addr in ('iab@iab.org', 'iab-chair@iab.org') and is_iabchair(person):
            return True
        elif addr in ('execd@iab.org', ) and is_iab_executive_director(person):
            return True
    return False


def liaison_detail(request, object_id):
    if settings.USE_DB_REDESIGN_PROXY_CLASSES:
        qfilter = Q()
        public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by("-submitted_date")
    else:
        qfilter = Q(approval__isnull=True)|Q(approval__approved=True)
        public_liaisons = LiaisonDetail.objects.filter(qfilter).order_by("-submitted_date")
    liaison = get_object_or_404(public_liaisons, pk=object_id)
    can_edit = False
    user = request.user
    can_take_care = _can_take_care(liaison, user)
    if user.is_authenticated() and can_edit_liaison(user, liaison):
        can_edit = True
    if request.method == 'POST' and request.POST.get('do_action_taken', None) and can_take_care:
        liaison.action_taken = True
        liaison.save()
        can_take_care = False
    relations = liaison.liaisondetail_set.filter(qfilter)
    return  object_detail(request,
                          public_liaisons,
                          template_name="liaisons/liaisondetail_detail.html",
                          object_id=object_id,
                          extra_context = {'can_edit': can_edit,
                                           'relations': relations,
                                           'can_take_care': can_take_care}
                         )

def liaison_edit(request, object_id):
    liaison = get_object_or_404(LiaisonDetail, pk=object_id)
    user = request.user
    if not (user.is_authenticated() and can_edit_liaison(user, liaison)):
        return HttpResponseForbidden('You have no permission to edit this liaison')
    return add_liaison(request, liaison=liaison)

def ajax_liaison_list(request):
    order = request.GET.get('order_by', 'submitted_date')
    plain_order = order
    reverse_order = order.startswith('-')
    if reverse_order:
        plain_order = order[1:]
    if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'):
        order = 'submitted_date'
        reverse_order = True
        plain_order = 'submitted_date'
    elif plain_order in ('submitted_date', 'deadline_date'):
        # Reverse order for date fields, humans find it more natural
        if reverse_order:
            order = plain_order
        else:
            order = '-%s' % plain_order
    
    if settings.USE_DB_REDESIGN_PROXY_CLASSES:
        public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order)
    else:
        public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order)

    return object_list(request, public_liaisons,
                       allow_empty=True,
                       template_name='liaisons/liaisondetail_simple_list.html',
                       extra_context={plain_order: not reverse_order and '-' or None}
                      )
