from django import forms
from django.contrib import messages
from django.db import transaction
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView
from django_select2.forms import Select2Widget

from byro.bookkeeping.models import (
    Account,
    Booking,
    DocumentTransactionLink,
    Transaction,
)

from .documents import DocumentUploadForm


class NewBookingForm(forms.Form):
    memo = forms.CharField(label=_("Memo"), max_length=1000, required=False)
    member = Booking._meta.get_field("member").formfield(widget=Select2Widget)
    account = Booking._meta.get_field("debit_account").formfield(widget=Select2Widget)
    debit_value = forms.DecimalField(
        min_value=0, max_digits=8, decimal_places=2, required=False
    )
    credit_value = forms.DecimalField(
        min_value=0, max_digits=8, decimal_places=2, required=False
    )


class TransactionDetailView(ListView):
    template_name = "office/transaction/detail.html"
    context_object_name = "bookings"
    model = Transaction
    paginate_by = None

    def get_form(self, input_data=None):
        form = NewBookingForm(input_data)
        form.fields["account"].required = True
        form.fields["member"].required = False
        t = self.get_object()
        if t.balances_credit != t.balances_debit:
            if t.balances_credit > t.balances_debit:
                form.fields["debit_value"].initial = (
                    t.balances_credit - t.balances_debit
                )
            else:
                form.fields["credit_value"].initial = (
                    t.balances_debit - t.balances_credit
                )
        return form

    def get_upload_form(self, input_data=None, input_files=None):
        form = DocumentUploadForm(
            input_data,
            input_files,
            prefix="upload_form",
            initial_category="byro.bookkeeping.receipt",
        )
        form.fields["title"].required = False
        return form

    def get_object(self):
        return Transaction.objects.with_balances().get(pk=self.kwargs["pk"])

    def get_queryset(self):
        return self.get_object().bookings.all()

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context["transaction"] = self.get_object()
        context["form"] = self.get_form()
        context["upload_form"] = self.get_upload_form()
        return context

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        t = self.get_object()
        form = self.get_form(request.POST)
        upload_form = self.get_upload_form(request.POST, request.FILES)

        if upload_form.is_valid():
            self.process_upload_form(upload_form, t)

        if form.is_valid():
            self.process_transaction_changes(form, t)
            t = self.get_object()

            if t.is_balanced:
                if "in_account" in request.GET:
                    account = Account.objects.get(pk=request.GET["in_account"])
                    if account.unbalanced_transactions.count():
                        return redirect(
                            "{}?filter=unbalanced".format(
                                reverse(
                                    "office:finance.accounts.detail",
                                    kwargs={"pk": account.pk},
                                )
                            )
                        )
                return redirect("office:finance.accounts.list")

        if "in_account" in request.GET:
            return redirect(
                "{}?in_account={}".format(
                    reverse("office:finance.transactions.detail", kwargs={"pk": t.pk}),
                    request.GET["in_account"],
                )
            )
        else:
            return redirect("office:finance.transactions.detail", pk=t.pk)

    def process_transaction_changes(self, form, t):
        arguments = dict(
            memo=form.cleaned_data["memo"],
            account=form.cleaned_data["account"],
            member=form.cleaned_data["member"],
            importer="_manual_entry",
            user_or_context=self,
        )
        if form.cleaned_data["debit_value"]:
            t.debit(amount=form.cleaned_data["debit_value"], **arguments)
        if form.cleaned_data["credit_value"]:
            t.credit(amount=form.cleaned_data["credit_value"], **arguments)
        t.save()
        messages.success(self.request, _("The transaction was updated."))
        t.log(self, ".updated")

    def process_upload_form(self, form, t):
        form.save()
        DocumentTransactionLink.objects.create(transaction=t, document=form.instance)
        t.log(
            self,
            ".document.created",
            document=form.instance,
            content_hash=form.instance.content_hash,
        )
