# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import json

from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, F
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView, DetailView
from django_tables2 import SingleTableView
from django.urls import reverse_lazy

from activity.models import Entry
from note_kfet.inputs import AmountInput
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin

from .forms import TransactionTemplateForm, SearchTransactionForm
from .models import TemplateCategory, Transaction, TransactionTemplate, RecurrentTransaction, NoteSpecial, Note
from .models.transactions import SpecialTransaction
from .tables import HistoryTable, ButtonTable


class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    View for the creation of Transaction between two note which are not :models:`transactions.RecurrentTransaction`.
    e.g. for donation/transfer between people and clubs or for credit/debit with :models:`note.NoteSpecial`
    """
    template_name = "note/transaction_form.html"

    model = Transaction
    # Transaction history table
    table_class = HistoryTable
    extra_context = {"title": _("Transfer money")}

    def get_queryset(self, **kwargs):
        return Transaction.objects.filter(
            PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
        ).order_by("-created_at").all()[:20]

    def get_context_data(self, **kwargs):
        """
        Add some context variables in template such as page title
        """
        context = super().get_context_data(**kwargs)
        context['amount_widget'] = AmountInput(attrs={"id": "amount"})
        context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
        context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
        context['special_types'] = NoteSpecial.objects\
            .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\
            .order_by("special_type").all()

        # Add a shortcut for entry page for open activities
        if "activity" in settings.INSTALLED_APPS:
            from activity.models import Activity
            activities_open = Activity.objects.filter(open=True).filter(
                PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all()
            context["activities_open"] = [a for a in activities_open
                                          if PermissionBackend.check_perm(self.request.user,
                                                                          "activity.add_entry",
                                                                          Entry(activity=a,
                                                                                note=self.request.user.note, ))]

        return context


class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Create Transaction template
    """
    model = TransactionTemplate
    form_class = TransactionTemplateForm
    success_url = reverse_lazy('note:template_list')
    extra_context = {"title": _("Create new button")}


class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    List Transaction templates
    """
    model = TransactionTemplate
    table_class = ButtonTable
    extra_context = {"title": _("Search button")}

    def get_queryset(self, **kwargs):
        """
        Filter the user list with the given pattern.
        """
        qs = super().get_queryset().distinct()
        if "search" in self.request.GET:
            pattern = self.request.GET["search"]
            qs = qs.filter(Q(name__iregex="^" + pattern) | Q(destination__club__name__iregex="^" + pattern))

        qs = qs.order_by('-display', 'category__name', 'destination__club__name', 'name')

        return qs


class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update Transaction template
    """
    model = TransactionTemplate
    form_class = TransactionTemplateForm
    success_url = reverse_lazy('note:template_list')
    extra_context = {"title": _("Update button")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        if "logs" in settings.INSTALLED_APPS:
            from logs.models import Changelog
            update_logs = Changelog.objects.filter(
                model=ContentType.objects.get_for_model(TransactionTemplate),
                instance_pk=self.object.pk,
                action="edit",
            )
            price_history = []
            for log in update_logs.all():
                old_dict = json.loads(log.previous)
                new_dict = json.loads(log.data)
                old_price = old_dict["amount"]
                new_price = new_dict["amount"]
                if old_price != new_price:
                    price_history.append(dict(price=old_price, time=log.timestamp))

            price_history.append(dict(price=self.object.amount, time=None))

            price_history.reverse()

            context["price_history"] = price_history

        return context


class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    The Magic View that make people pay their beer and burgers.
    (Most of the magic happens in the dark world of Javascript see consos.js)
    """
    model = Transaction
    template_name = "note/conso_form.html"
    extra_context = {"title": _("Consumptions")}

    # Transaction history table
    table_class = HistoryTable

    def get_queryset(self, **kwargs):
        return Transaction.objects.filter(
            PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
        ).order_by("-created_at").all()[:20]

    def get_context_data(self, **kwargs):
        """
        Add some context variables in template such as page title
        """
        context = super().get_context_data(**kwargs)
        categories = TemplateCategory.objects.order_by('name').all()
        for category in categories:
            category.templates_filtered = category.templates.filter(
                PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
            ).filter(display=True).order_by('name').all()
        context['categories'] = [cat for cat in categories if cat.templates_filtered]
        context['highlighted'] = TransactionTemplate.objects.filter(highlighted=True).filter(
            PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
        ).order_by('name').all()
        context['polymorphic_ctype'] = ContentType.objects.get_for_model(RecurrentTransaction).pk

        # select2 compatibility
        context['no_cache'] = True

        return context


class TransactionSearchView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    model = Note
    context_object_name = "note"
    template_name = "note/search_transactions.html"
    extra_context = {"title": _("Search transactions")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        form = SearchTransactionForm(data=self.request.GET if self.request.GET else None)
        context["form"] = form

        form.full_clean()
        if form.is_valid():
            data = form.cleaned_data
        else:
            data = {}

        transactions = Transaction.objects.annotate(total_amount=F("quantity") * F("amount")).filter(
            PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\
            .filter(Q(source=self.object) | Q(destination=self.object)).order_by('-created_at')

        if "source" in data and data["source"]:
            transactions = transactions.filter(source_id=data["source"].note_id)
        if "destination" in data and data["destination"]:
            transactions = transactions.filter(destination_id=data["destination"].note_id)
        if "type" in data and data["type"]:
            transactions = transactions.filter(polymorphic_ctype__in=data["type"])
        if "reason" in data and data["reason"]:
            transactions = transactions.filter(reason__iregex=data["reason"])
        if "valid" in data and data["valid"]:
            transactions = transactions.filter(valid=data["valid"])
        if "amount_gte" in data and data["amount_gte"]:
            transactions = transactions.filter(total_amount__gte=data["amount_gte"])
        if "amount_lte" in data and data["amount_lte"]:
            transactions = transactions.filter(total_amount__lte=data["amount_lte"])
        if "created_after" in data and data["created_after"]:
            transactions = transactions.filter(created_at__gte=data["created_after"])
        if "created_before" in data and data["created_before"]:
            transactions = transactions.filter(created_at__lte=data["created_before"])

        table = HistoryTable(transactions)
        table.paginate(per_page=100)
        context["table"] = table

        return context