from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import messages
from django.urls import reverse
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
import django_filters as filters

from admin_frontend.templatetags.public_urls import public_route
from admin_frontend.nav import _nav_items
from cms.views import breadcrumbs

from orgs.models import Organization
from recycle_core.models import Customer
from recycle_core.forms import PaymentForm

from .models import Invoice, Payment, Payout

from django.db.models import Sum, F, ExpressionWrapper, DecimalField, Value
from django.db.models.functions import Coalesce
from decimal import Decimal
from django.http import HttpResponse
import csv


# Invoices UI ---------------------------------------------------------------

@staff_member_required
@public_route(label="Invoices", order=50, icon="receipt-percent")
@breadcrumbs(label="Invoices", name="re_invoices")
def invoices_list(request):
    class InvoiceFilter(filters.FilterSet):
        organization = filters.ModelChoiceFilter(queryset=Organization.objects.all())
        customer = filters.ModelChoiceFilter(queryset=Customer.objects.all())
        status = filters.ChoiceFilter(field_name="status", choices=Invoice.STATUS_CHOICES)
        issued_at = filters.DateFromToRangeFilter(field_name="issued_at", label="Issued between")

        class Meta:
            model = Invoice
            fields = ["organization", "customer", "status", "issued_at"]

    base_qs = (
        Invoice.objects.select_related("organization", "customer")
        .annotate(
            payments_total=Coalesce(
                Sum("payments__amount"),
                Value(Decimal("0.00")),
                output_field=DecimalField(max_digits=14, decimal_places=2),
            ),
        )
        .annotate(
            balance=ExpressionWrapper(
                F("total_amount") - F("payments_total"),
                output_field=DecimalField(max_digits=14, decimal_places=2),
            )
        )
        .order_by("-issued_at", "-id")
    )
    i_filter = InvoiceFilter(request.GET, queryset=base_qs)
    qs = i_filter.qs
    org = getattr(request, "org", None)
    if org is not None:
        qs = qs.filter(organization=org)

    paginator = Paginator(qs, 10)
    page = request.GET.get("page")
    try:
        page_obj = paginator.page(page)
    except PageNotAnInteger:
        page_obj = paginator.page(1)
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)

    pay_form = PaymentForm()
    context = {
        "nav": _nav_items(),
        "invoices": page_obj.object_list,
        "page_obj": page_obj,
        "filter": i_filter,
        "pay_form": pay_form,
    }
    return render(request, "recycle_core/invoices_list.html", context)


@staff_member_required
def invoice_add_payment(request, pk: int):
    invoice = get_object_or_404(Invoice, pk=pk)
    form = PaymentForm(request.POST)
    if form.is_valid():
        Payment.objects.create(
            invoice=invoice,
            amount=form.cleaned_data["amount"],
            currency_code=invoice.currency_code,
            received_at=form.cleaned_data.get("received_at") or None,
            reference=form.cleaned_data.get("reference", ""),
        )
        messages.success(request, f"Payment recorded for invoice #{invoice.id}.")
    else:
        messages.error(request, "Invalid payment data.")
    return redirect("billing:invoices_list")


# Payouts UI + CSV ----------------------------------------------------------

@staff_member_required
@public_route(label="Payouts", order=52, icon="banknotes")
@breadcrumbs(label="Payouts", name="re_payouts")
def payouts_list(request):
    class PayoutFilter(filters.FilterSet):
        organization = filters.ModelChoiceFilter(queryset=Organization.objects.all())
        customer = filters.ModelChoiceFilter(queryset=Customer.objects.all())
        paid_at = filters.DateFromToRangeFilter(field_name="paid_at", label="Paid between")

        class Meta:
            model = Payout
            fields = ["organization", "customer", "paid_at"]

    base_qs = Payout.objects.select_related("organization", "customer").order_by("-paid_at", "-id")
    p_filter = PayoutFilter(request.GET, queryset=base_qs)
    qs = p_filter.qs
    org = getattr(request, "org", None)
    if org is not None:
        qs = qs.filter(organization=org)

    paginator = Paginator(qs, 10)
    page = request.GET.get("page")
    try:
        page_obj = paginator.page(page)
    except PageNotAnInteger:
        page_obj = paginator.page(1)
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)

    context = {
        "nav": _nav_items(),
        "payouts": page_obj.object_list,
        "page_obj": page_obj,
        "filter": p_filter,
    }
    return render(request, "recycle_core/payouts_list.html", context)


@staff_member_required
def payouts_export_csv(request):
    qs = Payout.objects.select_related("organization", "customer").all()
    org_obj = getattr(request, "org", None)
    if org_obj is not None:
        qs = qs.filter(organization=org_obj)
    org = request.GET.get("organization")
    cust = request.GET.get("customer")
    start = request.GET.get("paid_at_after") or request.GET.get("paid_at_min")
    end = request.GET.get("paid_at_before") or request.GET.get("paid_at_max")
    from django.utils.dateparse import parse_datetime

    if org:
        qs = qs.filter(organization_id=org)
    if cust:
        qs = qs.filter(customer_id=cust)
    if start:
        dt = parse_datetime(start)
        if dt:
            qs = qs.filter(paid_at__gte=dt)
    if end:
        dt = parse_datetime(end)
        if dt:
            qs = qs.filter(paid_at__lte=dt)

    response = HttpResponse(content_type="text/csv")
    response["Content-Disposition"] = "attachment; filename=payouts.csv"
    writer = csv.writer(response)
    writer.writerow(["organization", "customer", "amount", "currency", "paid_at", "reference", "pickup_id"]) 
    for p in qs.iterator():
        writer.writerow([
            p.organization.code,
            p.customer.name,
            p.amount,
            p.currency_code,
            p.paid_at.isoformat(),
            p.reference,
            p.pickup_id or "",
        ])
    return response
