import csv
import logging
import zipfile
from io import StringIO

import magic
from django.contrib import messages
from django.contrib.auth.models import User
from django.db.models import Count, Q, Sum
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils import timezone
from django.views.generic import (
    CreateView,
    DeleteView,
    DetailView,
    FormView,
    ListView,
    TemplateView,
    UpdateView,
)

from camps.mixins import CampViewMixin
from economy.models import (
    AccountingExport,
    Bank,
    BankAccount,
    BankTransaction,
    Chain,
    ClearhausSettlement,
    CoinifyBalance,
    CoinifyInvoice,
    CoinifyPayout,
    Credebtor,
    EpayTransaction,
    Expense,
    MobilePayTransaction,
    Reimbursement,
    Revenue,
    ZettleBalance,
    ZettleReceipt,
)
from economy.utils import (
    AccountingExporter,
    CoinifyCSVImporter,
    MobilePayCSVImporter,
    ZettleExcelImporter,
    import_clearhaus_csv,
    import_epay_csv,
)

from ..forms import (
    BankCSVForm,
    ClearhausSettlementForm,
    CoinifyCSVForm,
    EpayCSVForm,
    MobilePayCSVForm,
    ZettleUploadForm,
)
from ..mixins import EconomyTeamPermissionMixin

logger = logging.getLogger("bornhack.%s" % __name__)


################################
# CHAINS & CREDEBTORS


class ChainListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = Chain
    template_name = "chain_list_backoffice.html"

    def get_queryset(self, *args, **kwargs):
        """Annotate the total count and amount for expenses and revenues for all credebtors in each chain."""
        qs = Chain.objects.annotate(
            camp_expenses_amount=Sum(
                "credebtors__expenses__amount",
                filter=Q(credebtors__expenses__camp=self.camp),
                distinct=True,
            ),
            camp_expenses_count=Count(
                "credebtors__expenses",
                filter=Q(credebtors__expenses__camp=self.camp),
                distinct=True,
            ),
            camp_revenues_amount=Sum(
                "credebtors__revenues__amount",
                filter=Q(credebtors__revenues__camp=self.camp),
                distinct=True,
            ),
            camp_revenues_count=Count(
                "credebtors__revenues",
                filter=Q(credebtors__revenues__camp=self.camp),
                distinct=True,
            ),
        )
        return qs


class ChainDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = Chain
    template_name = "chain_detail_backoffice.html"
    slug_url_kwarg = "chain_slug"

    def get_queryset(self, *args, **kwargs):
        """Annotate the Chain object with the camp filtered expense and revenue info."""
        qs = super().get_queryset(*args, **kwargs)
        qs = qs.annotate(
            camp_expenses_amount=Sum(
                "credebtors__expenses__amount",
                filter=Q(credebtors__expenses__camp=self.camp),
                distinct=True,
            ),
            camp_expenses_count=Count(
                "credebtors__expenses",
                filter=Q(credebtors__expenses__camp=self.camp),
                distinct=True,
            ),
            camp_revenues_amount=Sum(
                "credebtors__revenues__amount",
                filter=Q(credebtors__revenues__camp=self.camp),
                distinct=True,
            ),
            camp_revenues_count=Count(
                "credebtors__revenues",
                filter=Q(credebtors__revenues__camp=self.camp),
                distinct=True,
            ),
        )
        return qs

    def get_context_data(self, *args, **kwargs):
        """Add credebtors, expenses and revenues to the context in camp-filtered versions."""
        context = super().get_context_data(*args, **kwargs)

        # include credebtors as a seperate queryset with annotations for total number and
        # amount of expenses and revenues
        context["credebtors"] = Credebtor.objects.filter(
            chain=self.get_object()
        ).annotate(
            camp_expenses_amount=Sum(
                "expenses__amount", filter=Q(expenses__camp=self.camp), distinct=True
            ),
            camp_expenses_count=Count(
                "expenses", filter=Q(expenses__camp=self.camp), distinct=True
            ),
            camp_revenues_amount=Sum(
                "revenues__amount", filter=Q(revenues__camp=self.camp), distinct=True
            ),
            camp_revenues_count=Count(
                "revenues", filter=Q(revenues__camp=self.camp), distinct=True
            ),
        )

        # Include expenses and revenues for the Chain in context as seperate querysets,
        # since accessing them through the relatedmanager returns for all camps
        context["expenses"] = Expense.objects.filter(
            camp=self.camp, creditor__chain=self.get_object()
        ).prefetch_related("responsible_team", "user", "creditor")
        context["revenues"] = Revenue.objects.filter(
            camp=self.camp, debtor__chain=self.get_object()
        ).prefetch_related("responsible_team", "user", "debtor")
        return context


class CredebtorDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = Credebtor
    template_name = "credebtor_detail_backoffice.html"
    slug_url_kwarg = "credebtor_slug"

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context["expenses"] = (
            self.get_object()
            .expenses.filter(camp=self.camp)
            .prefetch_related("responsible_team", "user", "creditor")
        )
        context["revenues"] = (
            self.get_object()
            .revenues.filter(camp=self.camp)
            .prefetch_related("responsible_team", "user", "debtor")
        )
        return context


################################
# EXPENSES


class ExpenseListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = Expense
    template_name = "expense_list_backoffice.html"

    def get_queryset(self, **kwargs):
        """
        Exclude unapproved expenses, they are shown seperately
        """
        queryset = super().get_queryset(**kwargs)
        return queryset.exclude(approved__isnull=True).prefetch_related(
            "creditor",
            "user",
            "responsible_team",
        )

    def get_context_data(self, **kwargs):
        """
        Include unapproved expenses seperately
        """
        context = super().get_context_data(**kwargs)
        context["unapproved_expenses"] = Expense.objects.filter(
            camp=self.camp, approved__isnull=True
        ).prefetch_related(
            "creditor",
            "user",
            "responsible_team",
        )
        return context


class ExpenseDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
    model = Expense
    template_name = "expense_detail_backoffice.html"
    fields = ["notes"]

    def form_valid(self, form):
        """
        We have two submit buttons in this form, Approve and Reject
        """
        expense = form.save()
        if "approve" in form.data:
            # approve button was pressed
            expense.approve(self.request)
        elif "reject" in form.data:
            # reject button was pressed
            expense.reject(self.request)
        else:
            messages.error(self.request, "Unknown submit action")
        return redirect(
            reverse("backoffice:expense_list", kwargs={"camp_slug": self.camp.slug})
        )


######################################
# REIMBURSEMENTS


class ReimbursementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = Reimbursement
    template_name = "reimbursement_list_backoffice.html"


class ReimbursementDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = Reimbursement
    template_name = "reimbursement_detail_backoffice.html"


class ReimbursementCreateUserSelectView(
    CampViewMixin, EconomyTeamPermissionMixin, ListView
):
    template_name = "reimbursement_create_userselect.html"

    def get_queryset(self):
        queryset = User.objects.filter(
            id__in=Expense.objects.filter(
                camp=self.camp,
                reimbursement__isnull=True,
                paid_by_bornhack=False,
                approved=True,
            )
            .values_list("user", flat=True)
            .distinct()
        )
        return queryset


class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
    model = Reimbursement
    template_name = "reimbursement_create.html"
    fields = ["notes", "paid"]

    def dispatch(self, request, *args, **kwargs):
        """Get the user from kwargs"""
        self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])

        # get response now so we have self.camp available below
        response = super().dispatch(request, *args, **kwargs)

        # return the response
        return response

    def get(self, request, *args, **kwargs):
        # does this user have any approved and un-reimbursed expenses?
        if not self.reimbursement_user.expenses.filter(
            reimbursement__isnull=True, approved=True, paid_by_bornhack=False
        ):
            messages.error(
                request, "This user has no approved and unreimbursed expenses!"
            )
            return redirect(
                reverse("backoffice:index", kwargs={"camp_slug": self.camp.slug})
            )
        return super().get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["expenses"] = Expense.objects.filter(
            user=self.reimbursement_user,
            approved=True,
            reimbursement__isnull=True,
            paid_by_bornhack=False,
        )
        context["total_amount"] = context["expenses"].aggregate(Sum("amount"))
        context["reimbursement_user"] = self.reimbursement_user
        return context

    def form_valid(self, form):
        """
        Set user and camp for the Reimbursement before saving
        """
        # get the expenses for this user
        expenses = Expense.objects.filter(
            user=self.reimbursement_user,
            approved=True,
            reimbursement__isnull=True,
            paid_by_bornhack=False,
        )
        if not expenses:
            messages.error(self.request, "No expenses found")
            return redirect(
                reverse(
                    "backoffice:reimbursement_list",
                    kwargs={"camp_slug": self.camp.slug},
                )
            )

        # do we have an Economy team for this camp?
        if not self.camp.economy_team:
            messages.error(self.request, "No economy team found")
            return redirect(
                reverse(
                    "backoffice:reimbursement_list",
                    kwargs={"camp_slug": self.camp.slug},
                )
            )

        # create reimbursement in database
        reimbursement = form.save(commit=False)
        reimbursement.reimbursement_user = self.reimbursement_user
        reimbursement.user = self.request.user
        reimbursement.camp = self.camp
        reimbursement.save()

        # add all expenses to reimbursement
        for expense in expenses:
            expense.reimbursement = reimbursement
            expense.save()

        # create payback expense for this reimbursement
        reimbursement.create_payback_expense()

        messages.success(
            self.request,
            f"Reimbursement {reimbursement} has been created with payback expense {reimbursement.payback_expense}"
            % (reimbursement.pk, timezone.now()),
        )
        return redirect(
            reverse(
                "backoffice:reimbursement_detail",
                kwargs={"camp_slug": self.camp.slug, "pk": reimbursement.pk},
            )
        )


class ReimbursementUpdateView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
    model = Reimbursement
    template_name = "reimbursement_form.html"
    fields = ["notes", "paid"]

    def get_success_url(self):
        return reverse(
            "backoffice:reimbursement_detail",
            kwargs={"camp_slug": self.camp.slug, "pk": self.get_object().pk},
        )


class ReimbursementDeleteView(CampViewMixin, EconomyTeamPermissionMixin, DeleteView):
    model = Reimbursement
    template_name = "reimbursement_delete.html"
    fields = ["notes", "paid"]

    def get(self, request, *args, **kwargs):
        if self.get_object().paid:
            messages.error(
                request,
                "This reimbursement has already been paid so it cannot be deleted",
            )
            return redirect(
                reverse(
                    "backoffice:reimbursement_list",
                    kwargs={"camp_slug": self.camp.slug},
                )
            )
        # continue with the request
        return super().get(request, *args, **kwargs)

    def get_success_url(self):
        messages.success(
            self.request, f"Reimbursement {self.kwargs['pk']} deleted successfully!"
        )
        return reverse(
            "backoffice:reimbursement_list",
            kwargs={"camp_slug": self.camp.slug},
        )


################################
# REVENUES


class RevenueListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = Revenue
    template_name = "revenue_list_backoffice.html"

    def get_queryset(self, **kwargs):
        """
        Exclude unapproved revenues, they are shown seperately
        """
        queryset = super().get_queryset(**kwargs)
        return queryset.exclude(approved__isnull=True).prefetch_related(
            "debtor",
            "user",
            "responsible_team",
        )

    def get_context_data(self, **kwargs):
        """
        Include unapproved revenues seperately
        """
        context = super().get_context_data(**kwargs)
        context["unapproved_revenues"] = Revenue.objects.filter(
            camp=self.camp, approved__isnull=True
        )
        return context


class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
    model = Revenue
    template_name = "revenue_detail_backoffice.html"
    fields = ["notes"]

    def form_valid(self, form):
        """
        We have two submit buttons in this form, Approve and Reject
        """
        revenue = form.save()
        if "approve" in form.data:
            # approve button was pressed
            revenue.approve(self.request)
        elif "reject" in form.data:
            # reject button was pressed
            revenue.reject(self.request)
        else:
            messages.error(self.request, "Unknown submit action")
        return redirect(
            reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
        )


################################
# BANK, ACCOUNTS, TRANSACTIONS


class BankListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = Bank
    template_name = "bank_list.html"


class BankDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = Bank
    template_name = "bank_detail.html"
    pk_url_kwarg = "bank_uuid"


class BankCSVUploadView(CampViewMixin, EconomyTeamPermissionMixin, FormView):
    form_class = BankCSVForm
    template_name = "bank_csv_upload_form.html"

    def setup(self, *args, **kwargs):
        super().setup(*args, **kwargs)
        self.bank = Bank.objects.get(pk=kwargs["bank_uuid"])

    def get_form_kwargs(self, *args, **kwargs):
        form_kwargs = super().get_form_kwargs(*args, **kwargs)
        form_kwargs["bank"] = self.bank
        return form_kwargs

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context["bank"] = self.bank
        return context

    def form_valid(self, form):
        for file_id, file_handle in form.files.items():
            account = BankAccount.objects.get(pk=file_id)
            csvdata = file_handle.read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=";", quotechar='"')
            imported = account.import_csv(reader)
            if imported:
                messages.success(
                    self.request,
                    f"Successfully imported {imported} new transactions for bank account {account.name} ({account.pk})",
                )
            else:
                messages.info(
                    self.request,
                    f"No new transactions were created for bank account {account.name} ({account.pk}). Transaction text descriptions may have been updated.",
                )
        return redirect(
            reverse(
                "backoffice:bank_detail",
                kwargs={"camp_slug": self.camp.slug, "bank_uuid": self.bank.pk},
            )
        )


class BankAccountDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = BankAccount
    template_name = "bankaccount_detail.html"
    pk_url_kwarg = "bankaccount_uuid"


class BankTransactionDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = BankTransaction
    template_name = "banktransaction_detail.html"
    pk_url_kwarg = "banktransaction_uuid"


################################
# COINIFY


class CoinifyDashboardView(CampViewMixin, EconomyTeamPermissionMixin, TemplateView):
    template_name = "coinify_dashboard.html"

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        try:
            latest = CoinifyBalance.objects.latest()
            context["balance"] = {
                "when": latest.date,
                "btc": latest.btc,
                "eur": latest.eur,
                "dkk": latest.dkk,
            }
        except CoinifyBalance.DoesNotExist:
            context["balance"] = None

        context["invoices"] = CoinifyInvoice.objects.count()
        context["payouts"] = CoinifyPayout.objects.count()
        context["balances"] = CoinifyBalance.objects.count()
        return context


class CoinifyInvoiceListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = CoinifyInvoice
    template_name = "coinifyinvoice_list.html"


class CoinifyPayoutListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = CoinifyPayout
    template_name = "coinifypayout_list.html"


class CoinifyBalanceListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = CoinifyBalance
    template_name = "coinifybalance_list.html"


class CoinifyCSVImportView(CampViewMixin, EconomyTeamPermissionMixin, FormView):
    form_class = CoinifyCSVForm
    template_name = "coinify_csv_upload_form.html"

    def form_valid(self, form):
        if "invoices" in form.files:
            csvdata = form.files["invoices"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=",", quotechar='"')
            created = CoinifyCSVImporter.import_coinify_invoice_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"Invoices CSV processed OK. Successfully imported {created} new Coinify invoices.",
                )
            else:
                messages.info(
                    self.request,
                    "Invoices CSV processed OK. No new Coinify invoices were created.",
                )

        if "payouts" in form.files:
            csvdata = form.files["payouts"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=",", quotechar='"')
            created = CoinifyCSVImporter.import_coinify_payout_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"Payouts CSV processed OK. Successfully imported {created} new Coinify payouts.",
                )
            else:
                messages.info(
                    self.request,
                    "Payouts CSV processed OK. No new Coinify payouts were created.",
                )

        if "balances" in form.files:
            csvdata = form.files["balances"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=",", quotechar='"')
            created = CoinifyCSVImporter.import_coinify_balance_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"Balances CSV processed OK. Successfully imported {created} new Coinify balances.",
                )
            else:
                messages.info(
                    self.request,
                    "Balances CSV processed OK. No new Coinify balances were created.",
                )

        return redirect(
            reverse(
                "backoffice:coinify_dashboard",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


################################
# EPAY / BAMBORA / CLEARHAUS


class EpayTransactionListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = EpayTransaction
    template_name = "epaytransaction_list.html"


class EpayCSVImportView(CampViewMixin, EconomyTeamPermissionMixin, FormView):
    form_class = EpayCSVForm
    template_name = "epay_csv_upload_form.html"

    def form_valid(self, form):
        if "transactions" in form.files:
            csvdata = form.files["transactions"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=";", quotechar='"')
            created = import_epay_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"ePay Transactions CSV processed OK. Successfully imported {created} new ePay Transactions.",
                )
            else:
                messages.info(
                    self.request,
                    "ePay Transactions CSV processed OK. No new ePay Transactions were created.",
                )

        return redirect(
            reverse(
                "backoffice:epaytransaction_list",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


class ClearhausSettlementListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = ClearhausSettlement
    template_name = "clearhaus_settlement_list.html"


class ClearhausSettlementDetailView(
    CampViewMixin, EconomyTeamPermissionMixin, DetailView
):
    model = ClearhausSettlement
    template_name = "clearhaus_settlement_detail.html"
    pk_url_kwarg = "settlement_uuid"
    context_object_name = "cs"


class ClearhausSettlementImportView(
    CampViewMixin, EconomyTeamPermissionMixin, FormView
):
    form_class = ClearhausSettlementForm
    template_name = "clearhaus_csv_upload_form.html"

    def form_valid(self, form):
        if "settlements" in form.files:
            csvdata = form.files["settlements"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=",", quotechar='"')
            created = import_clearhaus_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"Clearhaus Settlements CSV processed OK. Successfully imported {created} new Clearhaus Settlements.",
                )
            else:
                messages.info(
                    self.request,
                    "Clearhaus Settlements CSV processed OK. No new Clearhaus Settlements created, but some might have been updated.",
                )

        return redirect(
            reverse(
                "backoffice:clearhaussettlement_list",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


################################
# ZETTLE (iZettle)


class ZettleDashboardView(CampViewMixin, EconomyTeamPermissionMixin, TemplateView):
    template_name = "zettle_dashboard.html"

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        try:
            latest = ZettleBalance.objects.latest()
            context["balance"] = latest
        except ZettleBalance.DoesNotExist:
            context["balance"] = None

        context["receipts"] = ZettleReceipt.objects.count()
        context["balances"] = ZettleBalance.objects.count()
        return context


class ZettleReceiptListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = ZettleReceipt
    template_name = "zettlereceipt_list.html"


class ZettleBalanceListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = ZettleBalance
    template_name = "zettlebalance_list.html"


class ZettleDataImportView(CampViewMixin, EconomyTeamPermissionMixin, FormView):
    form_class = ZettleUploadForm
    template_name = "zettle_upload_form.html"

    def form_valid(self, form):
        if "balances" in form.files:
            df = ZettleExcelImporter.load_zettle_balances_excel(form.files["balances"])
            created = ZettleExcelImporter.import_zettle_balances_df(df)
            if created:
                messages.success(
                    self.request,
                    f"Zettle balances data processed OK. Successfully imported {created} new Zettle balances.",
                )
            else:
                messages.info(
                    self.request,
                    "Zettle balances data processed OK. No new Zettle balances created.",
                )

        if "receipts" in form.files:
            df = ZettleExcelImporter.load_zettle_receipts_excel(form.files["receipts"])
            created = ZettleExcelImporter.import_zettle_receipts_df(df)
            if created:
                messages.success(
                    self.request,
                    f"Zettle receipts data processed OK. Successfully imported {created} new Zettle receipts.",
                )
            else:
                messages.info(
                    self.request,
                    "Zettle receipts data processed OK. No new Zettle receipts created.",
                )

        return redirect(
            reverse(
                "backoffice:zettle_dashboard",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


################################
# MOBILEPAY


class MobilePayTransactionListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = MobilePayTransaction
    template_name = "mobilepaytransaction_list.html"


class MobilePayCSVImportView(CampViewMixin, EconomyTeamPermissionMixin, FormView):
    form_class = MobilePayCSVForm
    template_name = "mobilepay_csv_upload_form.html"

    def form_valid(self, form):
        if "transfers" in form.files:
            csvdata = form.files["transfers"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=";", quotechar='"')
            created = MobilePayCSVImporter.import_mobilepay_transfer_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"MobilePay Transfers/transactions CSV processed OK. Successfully imported {created} new MobilePay Transactions.",
                )
            else:
                messages.info(
                    self.request,
                    "MobilePay Transfers/transactions CSV processed OK. No new MobilePay Transactions created.",
                )

        if "sales" in form.files:
            csvdata = form.files["sales"].read().decode("utf-8-sig")
            reader = csv.reader(StringIO(csvdata), delimiter=";", quotechar='"')
            created = MobilePayCSVImporter.import_mobilepay_sales_csv(reader)
            if created:
                messages.success(
                    self.request,
                    f"MobilePay Sales CSV processed OK. Successfully imported {created} new MobilePay Transactions.",
                )
            else:
                messages.info(
                    self.request,
                    "MobilePay Sales CSV processed OK. No new MobilePay Transactions created.",
                )

        return redirect(
            reverse(
                "backoffice:mobilepaytransaction_list",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


################################
# ACCOUNTING EXPORT


class AccountingExportCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateView):
    template_name = "accountingexport_form.html"
    model = AccountingExport
    fields = ["date_from", "date_to", "comment"]

    def form_valid(self, form):
        """Gather data and create a zipfile with all of it."""
        # get the model instance
        export = form.save(commit=False)

        # initiate and run the exporter
        exporter = AccountingExporter(
            startdate=export.date_from,
            enddate=export.date_to,
        )
        exporter.doit()

        # add the archive to the model and save
        export.archive.save(
            f"bornhack_accounting_export_from_{export.date_from}_to_{export.date_to}_{export.uuid}.zip",
            exporter.archivedata,
        )

        # some feedback and redirect
        messages.success(
            self.request, f"Wrote archive file {export.archive.path} to disk, yay"
        )
        return redirect(
            reverse(
                "backoffice:accountingexport_list",
                kwargs={"camp_slug": self.camp.slug},
            )
        )


class AccountingExportListView(CampViewMixin, EconomyTeamPermissionMixin, ListView):
    model = AccountingExport
    template_name = "accountingexport_list.html"


class AccountingExportDetailView(CampViewMixin, EconomyTeamPermissionMixin, DetailView):
    model = AccountingExport
    template_name = "accountingexport_detail.html"
    pk_url_kwarg = "accountingexport_uuid"

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        z = zipfile.ZipFile(self.get_object().archive.path)
        # we need a list of just filenames without the folder name
        sorted_names = z.namelist()
        sorted_names.sort()
        context["files"] = [f.split("/")[1] for f in sorted_names if "/" in f]
        return context


class AccountingExportUpdateView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
    model = AccountingExport
    template_name = "accountingexport_form.html"
    pk_url_kwarg = "accountingexport_uuid"
    fields = ["comment"]

    def get_success_url(self):
        messages.success(
            self.request,
            f"Accounting Export {self.kwargs['accountingexport_uuid']} updated successfully!",
        )
        return reverse(
            "backoffice:accountingexport_list",
            kwargs={"camp_slug": self.camp.slug},
        )


class AccountingExportDeleteView(CampViewMixin, EconomyTeamPermissionMixin, DeleteView):
    model = AccountingExport
    template_name = "accountingexport_delete.html"
    pk_url_kwarg = "accountingexport_uuid"

    def get_success_url(self):
        messages.success(
            self.request,
            f"Accounting Export {self.kwargs['accountingexport_uuid']} deleted successfully!",
        )
        return reverse(
            "backoffice:accountingexport_list",
            kwargs={"camp_slug": self.camp.slug},
        )


class AccountingExportDownloadArchiveView(
    CampViewMixin, EconomyTeamPermissionMixin, DetailView
):
    model = AccountingExport
    pk_url_kwarg = "accountingexport_uuid"

    def get(self, request, *args, **kwargs):
        ae = self.get_object()
        archive = ae.archive.read()
        mimetype = magic.from_buffer(archive, mime=True)
        response = HttpResponse(content_type=mimetype)
        response[
            "Content-Disposition"
        ] = f"attachment; filename=bornhack_accounting_export_from_{ae.date_from}_to_{ae.date_to}_{ae.uuid}.zip"
        response.write(archive)
        return response


class AccountingExportDownloadFileView(
    CampViewMixin, EconomyTeamPermissionMixin, DetailView
):
    model = AccountingExport
    pk_url_kwarg = "accountingexport_uuid"

    def get(self, request, *args, **kwargs):
        ae = self.get_object()
        filename = kwargs["filename"]
        with zipfile.ZipFile(ae.archive.path) as z:
            with z.open("bornhack_accounting_export/" + filename) as f:
                data = f.read()
        mimetype = magic.from_buffer(data, mime=True)
        response = HttpResponse(content_type=mimetype)
        response["Content-Disposition"] = f"attachment; filename={filename}"
        response.write(data)
        return response
