from __future__ import unicode_literals
from datetime import datetime, time, timedelta

from django.template import RequestContext
from django.db.models import Sum, Q
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404, redirect
from django.http import HttpResponseRedirect
from django.conf import settings

from tendenci.core.base.http import Http403
from tendenci.core.theme.shortcuts import themed_response as render_to_response
from tendenci.core.perms.decorators import is_enabled
from tendenci.core.perms.utils import has_perm, update_perms_and_save
from tendenci.core.event_logs.models import EventLog
from tendenci.core.site_settings.utils import get_setting
from tendenci.apps.notifications.utils import send_notifications

from tendenci.core.payments.forms import MarkAsPaidForm
from tendenci.apps.invoices.utils import run_invoice_export_task
from tendenci.apps.invoices.models import Invoice
from tendenci.apps.invoices.forms import AdminNotesForm, AdminAdjustForm, InvoiceSearchForm


@is_enabled('invoices')
def view(request, id, guid=None, form_class=AdminNotesForm, template_name="invoices/view.html"):
    """
    Invoice information, payment attempts (successful and unsuccessful).
    """
    invoice = get_object_or_404(Invoice, pk=id)
    if not invoice.allow_view_by(request.user, guid):
        raise Http403

    allowed_tuple = (
        request.user.profile.is_superuser,
        has_perm(request.user, 'invoices.change_invoice'))

    form = None
    if any(allowed_tuple):
        if request.method == "POST":
            form = form_class(request.POST, instance=invoice)
            if form.is_valid():
                invoice = form.save()
                EventLog.objects.log(instance=invoice)
        else:
            form = form_class(initial={'admin_notes': invoice.admin_notes})

    notify = request.GET.get('notify', u'')
    guid = guid or u''

    merchant_login = (  # boolean value
        hasattr(settings, 'MERCHANT_LOGIN') and
        settings.MERCHANT_LOGIN)

    obj = invoice.get_object()
    obj_name = u''

    if obj:
        obj_name = obj._meta.verbose_name

    return render_to_response(template_name, {
        'invoice': invoice,
        'obj': obj,
        'obj_name': obj_name,
        'guid': guid,
        'notify': notify,
        'form': form,
        'merchant_login': merchant_login},
        context_instance=RequestContext(request))


def mark_as_paid(request, id, template_name='invoices/mark-as-paid.html'):
    """
    Makes a payment-record with a specified date/time
    payment method and payment amount.
    """
    invoice = get_object_or_404(Invoice, pk=id)

    if not has_perm(request.user, 'payments.change_payment'):
        raise Http403

    if request.method == 'POST':
        form = MarkAsPaidForm(request.POST)

        if form.is_valid():

            # make payment record
            payment = form.save(
                user=request.user,
                invoice=invoice,
                commit=False)

            payment = update_perms_and_save(request, form, payment)

            # update invoice; make accounting entries
            action_taken = invoice.make_payment(payment.creator,
                                                payment.amount)
            if action_taken:
                EventLog.objects.log(instance=invoice)
                messages.add_message(
                    request,
                    messages.SUCCESS,
                    'Payment successfully made')

            return redirect(invoice)

    else:
        form = MarkAsPaidForm(initial={
            'amount': invoice.balance, 'submit_dt': datetime.now()})

    return render_to_response(
        template_name, {
            'invoice': invoice,
            'form': form,
        }, context_instance=RequestContext(request))


def mark_as_paid_old(request, id):
    """
    Sets invoice balance to 0 and adds
    accounting entries
    """
    invoice = get_object_or_404(Invoice, pk=id)

    if not request.user.profile.is_superuser:
        raise Http403

    action_taken = invoice.make_payment(request.user, invoice.balance)

    if action_taken:
        EventLog.objects.log(instance=invoice)

        messages.add_message(
            request,
            messages.SUCCESS,
            'Successfully marked invoice %s as paid.' % invoice.pk)

    return redirect(invoice)


def void_payment(request, id):
    
    invoice = get_object_or_404(Invoice, pk=id)
    
    if not (request.user.profile.is_superuser): raise Http403    
    
    amount = invoice.payments_credits
    invoice.void_payment(request.user, amount)

    EventLog.objects.log(instance=invoice)

    messages.add_message(request, messages.SUCCESS, 'Successfully voided payment for Invoice %s.' % invoice.id)
    return redirect(invoice)


@is_enabled('invoices')
@login_required
def search(request, template_name="invoices/search.html"):
    start_amount = None
    end_amount = None
    tendered = None
    balance = None
    last_name = None
    start_dt = None
    end_dt = None
    search_criteria = None
    search_text = None
    search_method = None
    invoice_type = u''
    event = None
    event_id = None

    form = InvoiceSearchForm(request.GET)
    
    if form.is_valid():
        start_dt = form.cleaned_data.get('start_dt')
        end_dt = form.cleaned_data.get('end_dt')
        start_amount = form.cleaned_data.get('start_amount')
        end_amount = form.cleaned_data.get('end_amount')
        tendered = form.cleaned_data.get('tendered')
        balance = form.cleaned_data.get('balance')
        last_name = form.cleaned_data.get('last_name')
        search_criteria = form.cleaned_data.get('search_criteria')
        search_text = form.cleaned_data.get('search_text')
        search_method = form.cleaned_data.get('search_method')
        invoice_type = form.cleaned_data.get('invoice_type')
        event = form.cleaned_data.get('event')
        event_id = form.cleaned_data.get('event_id')

    invoices = Invoice.objects.all()
    if start_dt:
        invoices = invoices.filter(create_dt__gte=datetime.combine(start_dt, time.min))
    if end_dt:
        invoices = invoices.filter(create_dt__lte=datetime.combine(end_dt, time.max))

    if start_amount:
        invoices = invoices.filter(total__gte=start_amount)
    if end_amount:
        invoices = invoices.filter(total__lte=end_amount)

    if tendered:
        invoices = invoices.filter(status_detail=tendered)
    if balance == '0':
        invoices = invoices.filter(balance=0)
    elif balance == '1':
        invoices = invoices.filter(balance__gt=0)

    if last_name:
        invoices = invoices.filter(bill_to_last_name__iexact=last_name)
    
    owner = None
    if search_criteria and search_text:
        if search_criteria == 'owner_id':
            search_criteria = 'owner__id'
            try:
                search_text = int(search_text)
                [owner] = User.objects.filter(id=search_text)[:1] or [None]
            except:
                search_text = 0

        if search_method == 'starts_with':
            if isinstance(search_text, basestring):
                search_type = '__istartswith'
            else:
                search_type = '__startswith'
        elif search_method == 'contains':
            if isinstance(search_text, basestring):
                search_type = '__icontains'
            else:
                search_type = '__contains'
        else:
            if isinstance(search_text, basestring):
                search_type = '__iexact'
            else:
                search_type = '__exact'
  
        if all([search_criteria == 'owner__id',
                search_method == 'exact',
                owner]):
            invoices = invoices.filter(Q(bill_to_email__iexact=owner.email)
                               | Q(owner_id=owner.id))                    
        else: 
            search_filter = {'%s%s' % (search_criteria,
                                       search_type): search_text}
            invoices = invoices.filter(**search_filter)

    if invoice_type:
        content_type = ContentType.objects.filter(app_label=invoice_type)
        invoices = invoices.filter(object_type__in=content_type)
        if invoice_type == 'events':
            # Set event filters
            event_set = set()
            if event:
                event_set.add(event.pk)
            if event_id:
                event_set.add(event_id)
            if event or event_id:
                invoices = invoices.filter(registration__event__pk__in=event_set)
    
    if request.user.profile.is_superuser or has_perm(request.user, 'invoices.view_invoice'):
        invoices = invoices.order_by('-create_dt')
    else:
        invoices = invoices.filter(Q(creator=request.user) |
                                   Q(owner=request.user) |
                                   Q(bill_to_email__iexact=request.user.email)
                                   ).order_by('-create_dt')
    EventLog.objects.log()
    return render_to_response(template_name, {'invoices': invoices, 'form':form,}, 
        context_instance=RequestContext(request))


@login_required
def search_report(request, template_name="invoices/search.html"):
    from django.db.models import Q

    def is_number(num):
        try:
            float(num)
            return True
        except ValueError:
            return False

    #query
    query = request.GET.get('q', None)
    invoices = Invoice.objects.all()
    if query:
        if is_number(query):
            invoices = invoices.filter( Q(pk=query) | Q(total=query)
                                      | Q(balance=query)  | Q(title__icontains=query) )
        else:
            invoices = invoices.filter( Q(title__icontains=query) )
    #permissions
    if request.user.profile.is_superuser or has_perm(request.user, 'invoices.view_invoice'):
        invoices = invoices.order_by('object_type', '-create_dt')

        for i in invoices: #[0:2]:
            print i.title, i.object_id, i.object_type

            ct = ContentType.objects.get_for_model(Invoice)
            print ct, ct.__module__, ct.pk, i.pk

#            invoice_ids = invoices_objects.value_list('object_id', flat=True).filter(content_type = ct)
#            events = Invoice.objects.filter(pk__in=event_ids)

#            if i.object_type == 'registration':
#                print 'hello'
#            else: print 'not'
        print dir(ct)

    else:
        if request.user.is_authenticated():
            invoices = invoices.filter(Q(creator=request.user) | Q(owner=request.user)).order_by('-create_dt')
        else:
            raise Http403
    EventLog.objects.log()
    return render_to_response(template_name, {'invoices': invoices, 'query': query},
        context_instance=RequestContext(request))


@is_enabled('invoices')
def adjust(request, id, form_class=AdminAdjustForm, template_name="invoices/adjust.html"):
    #if not id: return HttpResponseRedirect(reverse('invoice.search'))
    invoice = get_object_or_404(Invoice, pk=id)
    original_total = invoice.total
    original_balance = invoice.balance

    if not (request.user.profile.is_superuser or has_perm(request.user, 'invoices.change_invoice')): raise Http403
    
    if request.method == "POST":
        form = form_class(request.POST, instance=invoice)
        if form.is_valid():
            invoice = form.save()
            invoice.total += invoice.variance 
            invoice.balance = invoice.total - invoice.payments_credits 
            invoice.save()
            
            EventLog.objects.log(instance=invoice)
            
            notif_context = {
                'request': request,
                'object': invoice,
            }
            send_notifications('module','invoices','invoicerecipients',
                'invoice_edited', notif_context)
            
            # make accounting entries
            from tendenci.apps.accountings.models import AcctEntry
            ae = AcctEntry.objects.create_acct_entry(request.user, 'invoice', invoice.id)
            if invoice.variance < 0:
                from tendenci.apps.accountings.utils import make_acct_entries_discount
                #this is a discount
                opt_d = {}
                opt_d['discount'] = True
                opt_d['original_invoice_total'] = original_total
                opt_d['original_invoice_balance'] = original_balance
                opt_d['discount_account_number'] = 460100
                
                obj = invoice.get_object()
                if obj and hasattr(obj, 'get_acct_number'):
                    opt_d['discount_account_number'] = obj.get_acct_number(discount=True)
                
                make_acct_entries_discount(request.user, invoice, ae, opt_d)
                
            else:
                from tendenci.apps.accountings.utils import make_acct_entries_initial
                make_acct_entries_initial(request.user, ae, invoice.variance)
            
            return HttpResponseRedirect(invoice.get_absolute_url())
            
    else:
        form = form_class(initial={'variance':0.00, 'variance_notes':invoice.variance_notes})
        
    return render_to_response(template_name, {'invoice': invoice,
                                              'form':form}, 
        context_instance=RequestContext(request))


@is_enabled('invoices')
def detail(request, id, template_name="invoices/detail.html"):
    invoice = get_object_or_404(Invoice, pk=id)

    allowed_list = (
        request.user.profile.is_superuser,
        has_perm(request.user, 'invoices.change_invoice')
    )

    if not any(allowed_list):
        raise Http403

    from tendenci.apps.accountings.models import AcctEntry
    acct_entries = AcctEntry.objects.filter(object_id=id)

    # to be calculated in accounts_tags
    total_debit, total_credit = 0, 0

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("""
        SELECT DISTINCT account_number, description, sum(amount) as total
        FROM accountings_acct
        INNER JOIN accountings_accttran on accountings_accttran.account_id =accountings_acct.id
        INNER JOIN accountings_acctentry on accountings_acctentry.id =accountings_accttran.acct_entry_id
        WHERE accountings_acctentry.object_id = %d
        GROUP BY account_number, description
        ORDER BY account_number  """ % (invoice.id))

    account_numbers = []
    for row in cursor.fetchall():
        account_numbers.append({
            "account_number": row[0],
            "description": row[1],
            "total": abs(row[2])})

    EventLog.objects.log(instance=invoice)

    print 'here'

    return render_to_response(template_name, {
        'invoice': invoice,
        'account_numbers': account_numbers,
        'acct_entries': acct_entries,
        'total_debit': total_debit,
        'total_credit': total_credit},
        context_instance=RequestContext(request))


@is_enabled('invoices')
@login_required
def export(request, template_name="invoices/export.html"):
    """Export Invoices"""

    if not request.user.is_superuser:
        raise Http403

    if request.method == 'POST':
        end_dt = request.POST.get('end_dt', None)
        start_dt = request.POST.get('start_dt', None)

        # First, convert our strings into datetime objects
        # in case we need to do a timedelta
        try:
            end_dt = datetime.strptime(end_dt, '%Y-%m-%d')
        except:
            end_dt = datetime.now()

        try:
            start_dt = datetime.strptime(start_dt, '%Y-%m-%d')
        except:
            start_dt = end_dt - timedelta(days=30)

        # convert our datetime objects back to strings
        # so we can pass them on to the task
        end_dt = end_dt.strftime("%Y-%m-%d")
        start_dt = start_dt.strftime("%Y-%m-%d")

        export_id = run_invoice_export_task('invoices', 'invoice', start_dt, end_dt)
        EventLog.objects.log()
        return redirect('export.status', export_id)
    else:
        end_dt = datetime.now()
        start_dt = end_dt - timedelta(days=30)

    return render_to_response(template_name, {
        'start_dt': start_dt,
        'end_dt': end_dt,
    }, context_instance=RequestContext(request))


@staff_member_required
def report_top_spenders(request, template_name="reports/top_spenders.html"):
    """Show dollars per user report"""
    if not request.user.is_superuser:
        raise Http403
    
    entry_list = []
    users = User.objects.all()
    for user in users:
        invoices = Invoice.objects.filter(Q(creator=user) | Q(owner=user) | Q(bill_to_email=user.email)).aggregate(Sum('total'))
        if invoices['total__sum'] is not None and invoices['total__sum'] > 0:
            entry_list.append({'user':user, 'invoices':invoices})

    entry_list = sorted(entry_list, key=lambda entry:entry['invoices']['total__sum'], reverse=True)[:20]

    return render_to_response(template_name, {
        'entry_list': entry_list,
    }, context_instance=RequestContext(request))
