# -*- coding: utf-8 -*-
import vouchers.models
from vouchers.models import ReimbursementRequest, Documentation
from finance_core.models import BudgetTerm, BudgetArea
from util.shortcuts import get_403_response

from django.contrib.auth.decorators import user_passes_test, login_required
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseNotAllowed
import django.forms
from django.forms import ChoiceField, Form, ModelForm, ModelChoiceField
from django.core.urlresolvers import reverse
from django.core.mail import send_mail, mail_admins, EmailMessage
from django.db.models import Q
from django.template import Context, Template
from django.template.loader import get_template
from django.views.decorators.csrf import ensure_csrf_cookie

import csv
import datetime
import decimal

import settings


class CommitteesField(ModelChoiceField):
    def __init__(self, *args, **kargs):
        base_area = BudgetArea.get_by_path(settings.BASE_COMMITTEE_PATH)
        self.strip_levels = base_area.depth
        areas = (base_area.get_descendants()
            .filter(depth__lte=base_area.depth+settings.COMMITTEE_HIERARCHY_LEVELS)
            .exclude(name='Holding')
        )
        ModelChoiceField.__init__(self, queryset=areas,
            help_text='Select the appropriate committe or other budget area',
            *args, **kargs)

    def label_from_instance(self, obj,):
        return obj.indented_name(strip_levels=self.strip_levels)

class SelectRequestBasicsForm(Form):
    area = CommitteesField()
    term = ModelChoiceField(queryset = BudgetTerm.objects.all())
    recipient_type = ChoiceField(choices=vouchers.models.recipient_type_choices)

class DocUploadForm(ModelForm):
    def clean_backing_file(self, ):
        f = self.cleaned_data['backing_file']
        ext = f.name.rsplit('.')[-1]
        contenttype = f.content_type
        if ext != 'pdf':
            raise django.forms.ValidationError(u"Only PDF files are accepted – you submitted a .%s file" % (ext, ))
        elif contenttype != 'application/pdf':
            raise django.forms.ValidationError(u"Only PDF files are accepted – you submitted a %s file" % (contenttype, ))
        else:
            return f

    class Meta:
        model = Documentation
        fields = (
            'label',
            'backing_file',
        )


@login_required
def select_request_basics(http_request, ):
    if http_request.method == 'POST': # If the form has been submitted...
        form = SelectRequestBasicsForm(http_request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            term = form.cleaned_data['term'].slug
            area = form.cleaned_data['area'].id
            recipient_type = form.cleaned_data['recipient_type']
            url = reverse(submit_request, args=[term, area, recipient_type],)
            return HttpResponseRedirect(url) # Redirect after POST
    else:
        form = SelectRequestBasicsForm() # An unbound form

    context = {
        'form':form,
        'pagename':'request_reimbursement',
    }
    return render_to_response('vouchers/select.html', context, context_instance=RequestContext(http_request), )


class CommitteeBudgetAreasField(ModelChoiceField):
    def __init__(self, base_area, *args, **kargs):
        self.strip_levels = base_area.depth
        areas = base_area.get_descendants()
        ModelChoiceField.__init__(self, queryset=areas,
            help_text='In general, this should be a fully indented budget area, not one with children',
            *args, **kargs)

    def label_from_instance(self, obj,):
        return obj.indented_name(strip_levels=self.strip_levels)

class ExpenseAreasField(ModelChoiceField):
    def __init__(self, *args, **kargs):
        base_area = vouchers.models.BudgetArea.get_by_path(['Accounts', 'Expenses'])
        self.strip_levels = base_area.depth
        areas = base_area.get_descendants()
        ModelChoiceField.__init__(self, queryset=areas,
            help_text='In general, this should be a fully indented budget area, not one with children',
            *args, **kargs)

    def label_from_instance(self, obj,):
        return obj.indented_name(strip_levels=self.strip_levels)

class RequestForm(ModelForm):
    expense_area = ExpenseAreasField()

    def __init__(self, *args, **kwargs):
        super(RequestForm, self).__init__(*args, **kwargs)
        if self.instance.recipient_type == 'mit':
            addr_widget = django.forms.HiddenInput()
            self.fields['check_to_addr'].widget = addr_widget
        else:
            rfp = vouchers.models.RFP
            error_msgs = dict(invalid=rfp.addr_error)
            addr_widget = self.fields['check_to_addr'].widget
            addr_field = django.forms.RegexField(regex=rfp.addr_regex, error_messages=error_msgs)
            addr_field.widget = addr_widget
            self.fields['check_to_addr'] = addr_field

    class Meta:
        model = ReimbursementRequest
        fields = (
            'name',
            'description',
            'incurred_time',
            'amount',
            'budget_area',
            'expense_area',
            'check_to_first_name',
            'check_to_last_name',
            'check_to_email',
            'check_to_addr',
        )

@login_required
def submit_request(http_request, term, committee, recipient_type, ):
    term_obj = get_object_or_404(BudgetTerm, slug=term)
    comm_obj = get_object_or_404(BudgetArea, pk=committee)

    new_request = ReimbursementRequest()
    new_request.submitter = http_request.user.username
    new_request.budget_term = term_obj
    new_request.recipient_type = recipient_type

    # Prefill from user information (itself prefilled from LDAP now)
    initial = {}
    initial['check_to_first_name'] = http_request.user.first_name
    initial['check_to_last_name']  = http_request.user.last_name
    initial['check_to_email']      = http_request.user.email

    if http_request.method == 'POST': # If the form has been submitted...
        form = RequestForm(http_request.POST, instance=new_request) # A form bound to the POST data
        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)

        if form.is_valid(): # All validation rules pass
            request_obj = form.save()
            print "request_obj==new_request:", request_obj == new_request

            # Send email
            tmpl = get_template('vouchers/emails/request_submit_admin.txt')
            ctx = Context({
                'submitter': http_request.user,
                'request': request_obj,
            })
            body = tmpl.render(ctx)
            recipients = []
            for name, addr in settings.ADMINS:
                recipients.append(addr)
            recipients.append(request_obj.budget_area.owner_address())
            if settings.CC_SUBMITTER:
                recipients.append(http_request.user.email)
            send_mail(
                '%sRequest submittal: %s requested $%s' % (
                    settings.EMAIL_SUBJECT_PREFIX,
                    http_request.user,
                    request_obj.amount,
                ),
                body,
                settings.SERVER_EMAIL,
                recipients,
            )

            return HttpResponseRedirect(reverse(review_request, args=[new_request.pk],) + '?new=true') # Redirect after POST
    else:
        form = RequestForm(instance=new_request, initial=initial, ) # An unbound form
        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)

    context = {
        'term':term_obj,
        'comm':comm_obj,
        'request': new_request,
        'form':form,
        'pagename':'request_reimbursement',
    }
    return render_to_response('vouchers/submit.html', context, context_instance=RequestContext(http_request), )

class VoucherizeForm(Form):
    name = django.forms.CharField(max_length=100, help_text='Signatory name for voucher',)
    email = django.forms.EmailField(max_length=100, help_text='Signatory email for voucher')


@login_required
def review_request(http_request, object_id):
    request_obj = get_object_or_404(ReimbursementRequest, pk=object_id)
    user = http_request.user
    pagename = 'request_reimbursement'
    new = False
    if 'new' in http_request.REQUEST:
        if http_request.REQUEST['new'].upper() == 'TRUE':
            new = True
        else:
            new = False

    if (user.has_perm('vouchers.can_list') or
        user.username == request_obj.submitter or
        user.email.upper() == request_obj.check_to_email.upper()
        ):
        pass
    else:
        return get_403_response(http_request, errmsg="You do not have permission to access this reimbursement request. You can only view requests you submitted or are the recipient for, unless you have general viewing permissions.", pagename=pagename, )

    # DOCUMENTATION #
    if request_obj.documentation:
        doc_upload_form = None
    else:
        new_docs = Documentation()
        new_docs.submitter = http_request.user.username
        if http_request.method == 'POST' and 'upload_documentation' in http_request.REQUEST: # If the form has been submitted...
            doc_upload_form = DocUploadForm(http_request.POST, http_request.FILES, instance=new_docs) # A form bound to the POST data

            if doc_upload_form.is_valid(): # All validation rules pass
                new_docs = doc_upload_form.save()
                request_obj.documentation = new_docs
                request_obj.save()

                return HttpResponseRedirect(reverse(review_request, args=[object_id],)) # Redirect after POST
        else:
            doc_upload_form = DocUploadForm(instance=new_docs, ) # An unbound form

    # SEND EMAILS
    show_email = http_request.user.has_perm('vouchers.can_email')
    if show_email:
        email_message = ''
        if http_request.method == 'POST' and 'send_email' in http_request.REQUEST:
            mail = vouchers.models.stock_emails[http_request.REQUEST['email_name']]
            assert mail.context == 'request'
            mail.send_email_request(request_obj)
            email_message = 'Sent email "%s".' % (mail.label, )
        email_options = []
        for mail in vouchers.models.stock_emails.values():
            if mail.context == 'request':
                email_options.append({
                    'label': mail.label,
                    'name' : mail.name,
                })

    # APPROVE VOUCHERS
    show_approve = (http_request.user.has_perm('vouchers.can_approve')
        and request_obj.approval_status == vouchers.models.APPROVAL_STATE_PENDING)
    post_approve = (http_request.method == 'POST' and 'approve' in http_request.REQUEST)
    approve_message = ''
    approve_level = ''
    if show_approve:
        if not request_obj.documentation:
            approve_message = "Documentation must be uploaded (above) before approving RFPs."
            approve_level = 'warn'
        elif post_approve:
            request_obj.approve_as_rfp(approver=http_request.user)
            approve_message = 'Queued RFP from request.'
            approve_level = 'info'
    elif post_approve:
        approve_message = "You attempted to approve a reimbursement request that you could not approve. Most likely, either the voucher has already been approved, or you do not have adequate permissions."
        approve_level = 'error'

    context = {
        'rr':request_obj,
        'pagename':pagename,
        'new': new,
        'doc_form': doc_upload_form,
        'show_approve': show_approve,
        'approve_message': approve_message,
        'approve_level': approve_level,
    }
    if show_email:
        context['email_options'] = email_options
        context['email_message'] = email_message
    return render_to_response('vouchers/ReimbursementRequest_review.html', context, context_instance=RequestContext(http_request), )

@user_passes_test(lambda u: u.has_perm('vouchers.generate_vouchers'))
def generate_vouchers(http_request, *args):
    unprocessed = True
    if 'unprocessed' in http_request.REQUEST:
        if http_request.REQUEST['unprocessed'].upper() == 'TRUE':
            unprocessed = True
        else:
            unprocessed = False
    mark = True
    if 'mark' in http_request.REQUEST:
        if http_request.REQUEST['mark'].upper() == 'TRUE':
            mark = True
        else:
            mark = False

    lst = vouchers.models.Voucher.objects.all()
    if unprocessed:
        lst = lst.filter(processed=False)

    total = decimal.Decimal('0.00')
    for voucher in lst:
        total = total + voucher.amount

    context = {
        'vouchers': lst,
        'total': total,
        'MEDIA_ROOT': settings.MEDIA_ROOT,
    }
    response = render_to_response(
        'vouchers/vouchers.tex',
        context, context_instance=RequestContext(http_request),
        mimetype=settings.LATEX_MIMETYPE,
    )

    # Send mail
    tmpl = get_template('vouchers/emails/vouchers_tex.txt')
    ctx = Context({
        'converter': http_request.user,
        'vouchers': lst,
        'mark': mark,
        'unprocessed': unprocessed,
    })
    body = tmpl.render(ctx)
    mail_admins(
        'Voucher rendering: %d by %s' % (
            len(lst),
            http_request.user,
        ),
        body,
    )

    if mark:
        for voucher in lst:
            voucher.mark_processed()

    return response

# not a view
def generate_rfp_specs_download(http_request):
    # Download unprocessed RFPs
    rfps = vouchers.models.RFP.objects.all()
    rfps = rfps.filter(rfp_number=None)
    response = HttpResponse(mimetype='text/csv')
    writer = csv.writer(response)
    cols = ['id', 'name',
        'payee.mit', 'payee.name',
        'addr.street', 'addr.city', 'addr.state', 'addr.postal',
        'item.date', 'item.gl', 'item.co', 'item.amount', 'item.desc',
        'documentation',
    ]
    writer.writerow(cols)
    for rfp in rfps:
        item_date = rfp.item_date.strftime("%m/%d/%Y")
        docs = http_request.build_absolute_uri(rfp.documentation.backing_file.url)
        writer.writerow((rfp.pk, rfp.name,
            rfp.payee_type == 'mit', rfp.payee_name,
            rfp.addr_street, rfp.addr_city, rfp.addr_state, rfp.addr_zip,
            item_date, rfp.item_gl, rfp.item_co, rfp.item_amount, rfp.item_desc,
            docs,
        ))

    # Send mail
    tmpl = get_template('vouchers/emails/rfps_download.txt')
    ctx = Context({
        'user': http_request.user,
        'rfps': rfps,
    })
    body = tmpl.render(ctx)
    mail_admins(
        'RFPs downloaded: %d by %s' % (
            len(rfps),
            http_request.user,
        ),
        body,
    )
    return response

def send_rfpized_email(rfp):
    # Should send mail to the submitter, including a coversheet to print and
    # give to SAO.  The attach() method of
    # https://docs.djangoproject.com/en/dev/topics/email/#emailmessage-objects
    # may be useful for the coversheet.
    tmpl = get_template('vouchers/emails/rfp_submitted.txt')
    ctx = Context({
        'rfp': rfp,
    })
    body = tmpl.render(ctx)
    to = [ rr.check_to_email for rr in rfp.reimbursementrequest_set.all() ]
    cc = [addr for name, addr in settings.ADMINS]
    mail = EmailMessage(
        subject="%sRFP submitted" % (settings.EMAIL_SUBJECT_PREFIX, ),
        body=body, to=to, cc=cc,
    )
    mail.send()

# not a view
def generate_rfp_specs_results(http_request):
    reader = csv.DictReader(http_request)
    rfps = vouchers.models.RFP.objects
    time = datetime.datetime.now()
    results = []
    dups = 0
    for line in reader:
        print line
        rfp = rfps.get(pk=int(line['id']))
        if not rfp.rfp_number:
            rfp.rfp_number = line['rfp_number']
            rfp.rfp_submit_time = time
            rfp.save()
            msg = "updated"
            send_rfpized_email(rfp)
        else:
            msg = "additional number: %s" % (line['rfp_number'], )
            dups += 1
        results.append((rfp, msg))

    # Send mail
    tmpl = get_template('vouchers/emails/rfps_updated.txt')
    ctx = Context({
        'user': http_request.user,
        'results': results,
        'dups': dups,
    })
    body = tmpl.render(ctx)
    mail_admins(
        'RFPs created: %d (%d duplicates) by %s' % (
            len(results), dups,
            http_request.user,
        ),
        body,
    )
    # For lack of something better to return, just print the email
    response = HttpResponse(body, mimetype='text/plain')
    return response

#@user_passes_test(lambda u: u.has_perm('vouchers.generate_vouchers'))
@ensure_csrf_cookie
def generate_rfp_specs(http_request):
    if http_request.method == 'GET':
        return generate_rfp_specs_download(http_request)
    elif http_request.method == 'POST':
        # Upload RFP processing results
        return generate_rfp_specs_results(http_request)
    else:
        content = "Expected GET (download unprocessed RFPs) or POST (upload results)"
        return HttpResponseNotAllowed(["GET", "POST"], content=content)

def get_related_requests_qobj(user, ):
    return Q(submitter=user.username) | Q(check_to_email=user.email)

request_list_orders = (
#   Name            Label               Columns
    ('default',     'Default',          ()),
    ('id',          'ID',               ('id', )),
    ('state',       'Approval Status',  ('approval_status', )),
    ('stateamount', 'Approval Status, then amount',  ('approval_status', 'amount', )),
    ('stateto',     'Approval Status, then recipient',  ('approval_status', 'check_to_first_name', 'check_to_last_name', )),
    ('statesubmit', 'Approval Status, then submitter',  ('approval_status', 'submitter', )),
    ('name',        'Request Name',     ('name', )),
    ('amount',      'Amount',           ('amount', )),
    ('check_to',    'Check Recipient',  ('check_to_first_name', 'check_to_last_name', )),
    ('submitter',   'Submitter',        ('submitter', )),
)

def list_to_keys(lst):
    dct = {}
    for key in lst:
        dct[key] = True
    return dct

@login_required
def show_requests(http_request, ):
    # BULK ACTIONS
    actions = vouchers.models.BulkRequestAction.filter_can_only(
        vouchers.models.bulk_request_actions,
        http_request.user,
    )
    apply_action_message = None
    apply_action_errors = []
    if 'select' in http_request.REQUEST:
        selected_rr_ids = [ int(item) for item in http_request.REQUEST.getlist('select') ]
    else:
        selected_rr_ids = []
    if "apply-action" in http_request.POST:
        action_name = http_request.POST['action']
        if action_name == 'none':
            apply_action_message = "No action selected."
        else:
            matching_actions = [ action for action in actions if action.name == action_name]
            if(len(matching_actions) > 0):
                action = matching_actions[0]
                rrs = ReimbursementRequest.objects.filter(pk__in=selected_rr_ids)
                for rr in rrs:
                    success, msg = action.do(http_request, rr)
                    if not success:
                        apply_action_errors.append((rr, msg))
                apply_action_message = '"%s" applied to %d request(s) (%d errors encountered)' % (action.label, len(rrs), len(apply_action_errors), )
            else:
                apply_action_message = "Unknown or forbidden action requested."

    # PERMISSION-BASED REQUEST FILTERING
    if http_request.user.has_perm('vouchers.can_list'):
        qs = ReimbursementRequest.objects.all()
        useronly = False
    else:
        qs = ReimbursementRequest.objects.filter(get_related_requests_qobj(http_request.user))
        useronly = True

    # SORTING
    if 'order' in http_request.REQUEST:
        order_row = [row for row in request_list_orders if row[0] == http_request.REQUEST['order']]
        if order_row:
            order, label, cols = order_row[0]
            qs = qs.order_by(*cols)
        else:
            raise Http404('Order by constraint not known')
    else:
        order = 'default'

    # DISCRETIONARY REQUEST FILTERING
    if 'approval_status' in http_request.REQUEST:
        approval_status = http_request.REQUEST['approval_status']
    else:
        approval_status = vouchers.models.APPROVAL_STATE_PENDING
    if approval_status == 'all':
        pass
    else:
        try:
            approval_status = int(approval_status)
        except ValueError:
            raise Http404('approval_status poorly formatted')
        state_row = [row for row in vouchers.models.APPROVAL_STATES if row[0] == approval_status]
        if state_row:
            qs = qs.filter(approval_status=approval_status)
        else:
            raise Http404('approval_status not known')

    # GENERATE THE REQUEST

    context = {
        'object_list' : qs,
        'actions' : actions,
        'selected_ids'  : list_to_keys(selected_rr_ids),
        'action_message': apply_action_message,
        'action_errors' : apply_action_errors,
        'useronly': useronly,
        'order'   : order,
        'orders'  : request_list_orders,
        'approval_status' : approval_status,
        'approval_states':  vouchers.models.APPROVAL_STATES,
        'pagename': 'list_requests',
    }
    return render_to_response('vouchers/reimbursementrequest_list.html', context, context_instance=RequestContext(http_request), )
