from decimal import Decimal
from django.db.models import Sum
from home.models import CompanyProfile
from . models import *
from django.shortcuts import render, get_object_or_404
from datetime import date, datetime
from django.utils import formats
from django.contrib.auth.decorators import login_required
from dateutil.parser import parse
from django.core.paginator import Paginator
from .models import Invoice
from django.contrib.auth.decorators import login_required, permission_required


from django.db.models import Sum


@permission_required('cargo.sales_report', raise_exception=True)
def invoice_report_view(request):
    invoices = Invoice.objects.select_related(
        'sender', 'receiver').order_by('-created_at')

    return render(request, "cargo/invoice_report.html", {
        "invoices": invoices,
    })


@login_required
@permission_required('cargo.expense_report', raise_exception=True)
def expense_report_view(request):
    expenses = AddExpense.objects.select_related(
        'expense_category', 'currency').order_by('-date')
    return render(request, 'cargo/expense_report.html', {
        'expenses': expenses
    })


def generate_invoice(request, pk):
    company_profile = CompanyProfile.objects.first()
    company_rules = company_profile.companyrules_set.all()
    invoice = get_object_or_404(
        Invoice.objects.select_related('sender', 'receiver'), pk=pk)

    items_with_total_amount = []
    handling_charges = 0

    for item in invoice.invoiceitems_set.all():
        total_amount = item.quantity * item.unit_price
        items_with_total_amount.append({
            'item': item,
            'total_amount': total_amount
        })
        handling_charges += item.handling_charges

    sub_total = sum(item_with_total['total_amount']
                    for item_with_total in items_with_total_amount)

    grand_total = sub_total + handling_charges

    # Retrieve the previous balance from the latest SenderBalance record
    sender_balance = SenderBalance.objects.filter(
        sender=invoice.sender
    ).order_by('-id').first()

    sender_balance2 = SenderBalance.objects.filter(
        sender=invoice.sender
    ).order_by('-id')[1:2].first()

    previous_balance = sender_balance.current_balance if sender_balance else 0
    previous_balance2 = sender_balance2.current_balance if sender_balance2 else 0
    previous_balance += (previous_balance2-previous_balance)
    balance = sub_total + handling_charges

    grand_total += previous_balance
    # Calculate the balance
    balance = (balance + previous_balance) - invoice.amount_paid
    balance = max(balance, 0)

    formatted_created_at = formats.date_format(invoice.created_at, "Y F d")
    context = {
        'invoice': invoice,
        'formatted_created_at': formatted_created_at,
        'company_profile': company_profile,
        'company_rules': company_rules,
        'items_with_total_amount': items_with_total_amount,
        'handling_charges': handling_charges,
        'sub_total': sub_total,
        'grand_total': grand_total,
        'previous_balance': previous_balance,
        'balance': balance,
    }
    return render(request, 'cargo/invoice.html', context)


def delivery_details(request, pk):
    company_profile = CompanyProfile.objects.first()
    company_rules = company_profile.companyrules_set.all()
    invoice = get_object_or_404(
        Invoice.objects.select_related('sender', 'receiver'), pk=pk)
    formatted_created_at = formats.date_format(invoice.created_at, "Y F d")
    context = {
        'invoice': invoice,
        'formatted_created_at': formatted_created_at,
        'company_profile': company_profile,
    }
    return render(request, 'cargo/delivery_details.html', context)


def payment_to_expo_detail(request, pk):
    company_profile = CompanyProfile.objects.first()
    current_date = date.today()
    payment = get_object_or_404(PaymentToExpo, pk=pk)
    context = {
        'company_profile': company_profile,
        'payment': payment,
        'current_date': current_date,
    }
    return render(request, 'cargo/payment_to_expo_voucher.html', context)


def received_payment_from_sender_detail(request, pk):
    company_profile = CompanyProfile.objects.first()
    current_date = date.today()
    payment = get_object_or_404(ReceivePaymentFromSender, pk=pk)
    context = {
        'company_profile': company_profile,
        'payment': payment,
        'current_date': current_date,
    }
    return render(request, 'cargo/received_payment_from_sender_voucher.html', context)


def cargo_balance_view(request, pk):
    if request.method == 'POST':
        start_date = request.POST.get('start_date')
        end_date = request.POST.get('end_date')

        current_date = date.today()
        cargo = CargoBy.objects.get(id=pk)
        cargo_balance = CargoByBalance.objects.filter(
            cargo_by=pk).order_by('id')

        if start_date and end_date:
            try:
                start_date = parse(start_date).date()
                end_date = parse(end_date).date()
            except ValueError:
                start_date = None
                end_date = None
            else:
                cargo_balance = cargo_balance.filter(
                    tx_date__range=(start_date, end_date))

        if not cargo_balance:
            context = {
                'cargo': cargo,
                'start_date': start_date,
                'end_date': end_date
            }
            return render(request, 'cargo/no_balance.html', context)
        # Aggregate the debit, credit, and current_balance fields for the queryset
        totals = cargo_balance.aggregate(
            total_amount_in=Sum('amount_in'),
            total_amount_out=Sum('amount_out'),
            total_balance=Sum('current_balance')
        )
        from_date = cargo_balance.first().tx_date
        to_date = cargo_balance.last().tx_date
        net_balance = totals['total_amount_in'] - totals['total_amount_out']

        context = {
            'current_date': current_date,
            'cargo': cargo,
            'from_date': from_date,
            'to_date': to_date,
            'totals': totals,
            'balance': cargo_balance,
            'total_debit': totals['total_amount_out'],
            'total_credit': totals['total_amount_in'],
            'net_balance': net_balance,
            'total_balance': totals['total_balance'],
        }

        return render(request, 'cargo/print_cargo_balance.html', context)
    context = {
        'pk': pk
    }
    return render(request, 'cargo/date_parameter_cargo_balance.html', context)


def sender_balance_view(request, pk):
    current_date = date.today()
    sender = Sender.objects.get(id=pk)
    sender_balance = SenderBalance.objects.filter(
        sender=pk).order_by('id')

    if not sender_balance:
        context = {'sender': sender}
        return render(request, 'cargo/sender_no_balance.html', context)
    # Aggregate the debit, credit, and current_balance fields for the queryset
    totals = sender_balance.aggregate(
        total_amount_in=Sum('amount_in'),
        total_amount_out=Sum('amount_out'),
        total_balance=Sum('current_balance')
    )
    from_date = sender_balance.first().tx_date
    to_date = sender_balance.last().tx_date
    net_balance = totals['total_amount_in'] - totals['total_amount_out']

    context = {
        'current_date': current_date,
        'consumer': sender,
        'from_date': from_date,
        'to_date': to_date,
        'totals': totals,
        'balance':  sender_balance,
        'total_debit': totals['total_amount_out'],
        'total_credit': totals['total_amount_in'],
        'net_balance': net_balance,
        'total_balance': totals['total_balance'],
    }

    return render(request, 'cargo/print_sender_balance.html', context)


@login_required
def expense_voucher(request, pk):
    current_date = date.today()
    company_profile = CompanyProfile.objects.first()
    expense = get_object_or_404(AddExpense, pk=pk)
    user = request.user  # Get the currently logged-in user

    # Prepare the context dictionary with data to pass to the template
    context = {
        'expense': expense,
        'company_profile': company_profile,
        'current_date': current_date,
        'user': user,  # Include the user details in the context
    }

    # Render the 'finance/expense_voucher.html' template with the context data and return the rendered response
    return render(request, 'cargo/expense_voucher.html', context)


def expense_report_parametrized(request):
    current_date = date.today()
    start_date_persian = None
    end_date_persian = None

    if request.method == 'POST':
        start_date = request.POST.get('start_date')
        end_date = request.POST.get('end_date')
        approved_by_choices = request.POST.getlist('approved-by-choice')
        expense_category_choices = request.POST.getlist(
            'expense_category-choice')

        if start_date and end_date:
            try:
                start_date = parse(start_date).date()
                end_date = parse(end_date).date()
                start_date_persian = start_date
                end_date_persian = end_date
            except ValueError:
                start_date = None
                end_date = None

        expenses = AddExpense.objects.all()

        if expense_category_choices:
            expenses = expenses.filter(
                expense_category__in=expense_category_choices)
        if start_date and end_date:
            expenses = expenses.filter(date__range=[
                                       start_date, end_date])

        total_amount = expenses.aggregate(Sum('amount'))['amount__sum'] or 0

        context = {
            'expenses': expenses,
            'current_date': current_date,
            'approved_by_choices': approved_by_choices,
            'start_date': start_date_persian,
            'end_date': end_date_persian,
            'total_amount': total_amount
        }

        return render(request, 'cargo/expense_report_print.html', context)

    expense_category = ExpenseCategory.objects.all()

    context = {
        'expense_category': expense_category,
    }

    return render(request, 'cargo/expense_report_parameter.html', context)


def sales_list(request):
    sales = Invoice.objects.all().prefetch_related('invoiceitems_set')

    # Calculate the sums
    cargo_delivery_charges_sum = sales.aggregate(Sum('invoiceitems__deliver_charges'))[
        'invoiceitems__deliver_charges__sum'] or Decimal('0')

    # Calculate the expo_amount_sum using Python's sum function
    expo_amount_sum = sum(
        item.expo_amount for invoice in sales for item in invoice.invoiceitems_set.all())

    discount_sum = sales.aggregate(Sum('invoiceitems__discount'))[
        'invoiceitems__discount__sum'] or Decimal('0')

    # Calculate the total_amount_sum using Python's sum function
    total_amount_sum = sum(
        item.total_amount for invoice in sales for item in invoice.invoiceitems_set.all())
    net_amount_sum = sum(
        item.net_amount for invoice in sales for item in invoice.invoiceitems_set.all())

    context = {
        'sales': sales,
        'cargo_delivery_charges_sum': cargo_delivery_charges_sum,
        'expo_amount_sum': expo_amount_sum,
        'discount_sum': discount_sum,
        'total_amount_sum': total_amount_sum,
        'net_amount_sum': net_amount_sum,
    }

    return render(request, 'cargo/sales_list.html', context)
