from decimal import Decimal
from django import forms
from django.contrib import admin
from django.core import signing
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import IntegrityError
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.html import format_html
from . import payment_queries as q, payment_services as s
from .payment_models import METHODS, PAYMENT_PRIVACY
from .models import ServicePayment, ServicePaymentReceipt, PaymentReversal, ServiceFinancialRelease


class ReceiveForm(forms.Form):
    amount = forms.DecimalField(min_value=Decimal("0.01"), max_digits=14, decimal_places=2)
    method = forms.ChoiceField(choices=METHODS)
    reference = forms.CharField(max_length=200, required=False, help_text=PAYMENT_PRIVACY)
    note = forms.CharField(max_length=2000, required=False, widget=forms.Textarea, help_text=PAYMENT_PRIVACY)


class ReverseForm(forms.Form):
    payment = forms.ModelChoiceField(queryset=ServicePayment.objects.none())
    reason = forms.CharField(max_length=2000, widget=forms.Textarea, help_text=PAYMENT_PRIVACY)


class ReleaseForm(forms.Form):
    reason = forms.CharField(max_length=2000, widget=forms.Textarea, help_text=PAYMENT_PRIVACY)
    reference = forms.CharField(max_length=200, required=False, help_text=PAYMENT_PRIVACY)


def settlement_view(request, object_id):
    if not request.user.has_perm("commercial.view_servicepayment"):
        raise PermissionDenied
    invoice = get_object_or_404(q.settlement_invoices(actor=request.user), pk=object_id)
    state = q._summary(invoice)
    token = [str(invoice.pk), state["revision"], str(request.user.pk)]
    operation = request.POST.get("operation") if request.method == "POST" else None
    receive = ReceiveForm(request.POST if operation == "receive" else None)
    reversal = ReverseForm(request.POST if operation == "reverse" else None)
    reversal.fields["payment"].queryset = q.payments_for_invoice(actor=request.user, invoice=invoice).filter(status="POSTED")
    release = ReleaseForm(request.POST if operation == "release" else None)
    error = None
    if request.method == "POST":
        native = {"receive":"commercial.add_servicepayment", "reverse":"commercial.change_servicepayment", "release":"commercial.add_servicefinancialrelease"}
        if operation not in native or not request.user.has_perm(native[operation]):
            raise PermissionDenied
        try:
            if signing.loads(request.POST.get("revision_token", ""), salt="payment-workflow") != token:
                raise ValidationError("Settlement changed; reload and review the current balance.")
            args = dict(actor=request.user, expected_revision=state["revision"])
            if operation == "receive" and receive.is_valid():
                s.receive_service_payment(**args, invoice=invoice, **receive.cleaned_data)
            elif operation == "reverse" and reversal.is_valid():
                s.reverse_service_payment(**args, **reversal.cleaned_data)
            elif operation == "release" and release.is_valid():
                s.authorize_due_release(**args, invoice=invoice, **release.cleaned_data)
            else:
                raise ValidationError("Review the financial form errors.")
            return HttpResponseRedirect(reverse("admin:commercial_invoice_settlement", args=[invoice.pk]))
        except (ValidationError, IntegrityError, signing.BadSignature) as exc:
            error = "; ".join(exc.messages) if isinstance(exc, ValidationError) else "Invalid or conflicting financial submission."
    return TemplateResponse(request, "admin/commercial/payment_workflow.html", {**admin.site.each_context(request),
        "opts": ServicePayment._meta, "title": f"Settlement: {invoice.number}", "invoice": invoice, "state": state,
        "payments": q.payments_for_invoice(actor=request.user, invoice=invoice), "releases": q.financial_release_history(actor=request.user, invoice=invoice),
        "receive_form": receive, "reverse_form": reversal, "release_form": release, "error": error,
        "revision_token": signing.dumps(token, salt="payment-workflow")}, status=400 if error else 200)


class FinancialEvidenceAdmin(admin.ModelAdmin):
    actions = None
    def has_add_permission(self, request): return False
    def has_change_permission(self, request, obj=None): return False
    def has_delete_permission(self, request, obj=None): return False
    def get_readonly_fields(self, request, obj=None): return tuple(f.name for f in self.model._meta.concrete_fields)


@admin.register(ServicePayment)
class PaymentAdmin(FinancialEvidenceAdmin):
    list_display = ("invoice", "amount", "currency", "method", "status", "received_by", "received_at", "settlement")
    list_filter = ("status", "method")
    def get_queryset(self, request): return q.service_payments(actor=request.user)
    def settlement(self, obj):
        return format_html('<a href="{}">Settlement and receipts</a>', reverse("admin:commercial_invoice_settlement", args=[obj.invoice_id]))


@admin.register(ServicePaymentReceipt)
class ReceiptAdmin(FinancialEvidenceAdmin):
    list_display = ("number", "amount", "currency", "method", "issued_at")
    def get_queryset(self, request): return q.receipt_history(actor=request.user)


@admin.register(PaymentReversal)
class ReversalAdmin(FinancialEvidenceAdmin):
    def get_queryset(self, request):
        return super().get_queryset(request).filter(payment_id__in=q.service_payments(actor=request.user).values("pk")).select_related("payment", "reversed_by")


@admin.register(ServiceFinancialRelease)
class FinancialReleaseAdmin(FinancialEvidenceAdmin):
    def get_queryset(self, request):
        return super().get_queryset(request).filter(invoice_id__in=q.settlement_invoices(actor=request.user).values("pk")).select_related("invoice", "authorized_by")
