from django.contrib import admin, messages
from django.forms import Textarea
from django.http import HttpResponse, HttpResponseRedirect
from django.db.models import TextField, Q
from django.urls import path, reverse
from functools import update_wrapper
from django.utils.translation import gettext_lazy as _
from django.shortcuts import render
from django.conf import settings

from .models import Ledger, Statement, Receipt, Transaction, Bill, StatementSubmitted, StatementConfirmed,\
        StatementUnSubmitted


@admin.register(Ledger)
class LedgerAdmin(admin.ModelAdmin):
    search_fields = ('name', )


class BillOnStatementInline(admin.TabularInline):
    model = Bill
    extra = 0
    sortable_options = []
    fields = ['short_description', 'explanation', 'amount', 'paid_by', 'proof']
    formfield_overrides = {
        TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
    }

    def get_readonly_fields(self, request, obj=None):
        if obj is not None and obj.submitted:
            return self.fields
        return super(BillOnStatementInline, self).get_readonly_fields(request, obj)


@admin.register(StatementUnSubmitted)
class StatementUnSubmittedAdmin(admin.ModelAdmin):
    fields = ['short_description', 'explanation', 'excursion', 'submitted']
    list_display = ['__str__', 'excursion']
    inlines = [BillOnStatementInline]

    def save_model(self, request, obj, form, change):
        if not change and hasattr(request.user, 'member'):
            obj.created_by = request.user.member
        super().save_model(request, obj, form, change)

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        if request.user.has_perm('members.may_list_all_statements'):
            return queryset

        if not hasattr(request.user, 'member'):
            return Member.objects.none()

        return queryset.filter(Q(created_by=request.user.member) | Q(excursion__jugendleiter=request.user.member))

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = ['submitted', 'excursion']
        if obj is not None and obj.submitted:
            return readonly_fields + self.fields
        else:
            return readonly_fields

    def get_urls(self):
        urls = super().get_urls()

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)

            wrapper.model_admin = self
            return update_wrapper(wrapper, view)

        custom_urls = [
            path(
                "<path:object_id>/submit/",
                wrap(self.submit_view),
                name="%s_%s_submit" % (self.opts.app_label, self.opts.model_name),
            ),
        ]
        return custom_urls + urls

    def submit_view(self, request, object_id):
        statement = Statement.objects.get(pk=object_id)
        if statement.submitted:
            messages.error(request,
                    _("%(name)s is already submitted.") % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))

        if "apply" in request.POST:
            statement.submit(get_member(request))
            messages.success(request,
                    _("Successfully submited %(name)s. The finance department will notify the requestors as soon as possible.") % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
        context = dict(self.admin_site.each_context(request),
                       title=_('Submit statement'),
                       opts=self.opts,
                       statement=statement)

        return render(request, 'admin/submit_statement.html', context=context)


class TransactionOnSubmittedStatementInline(admin.TabularInline):
    model = Transaction
    fields = ['amount', 'member', 'reference', 'ledger']
    formfield_overrides = {
        TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
    }
    extra = 0


class BillOnSubmittedStatementInline(BillOnStatementInline):
    model = Bill
    extra = 0
    sortable_options = []
    fields = ['short_description', 'explanation', 'amount', 'paid_by', 'proof', 'costs_covered']
    formfield_overrides = {
        TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
    }

    def get_readonly_fields(self, request, obj=None):
        return ['short_description', 'explanation', 'amount', 'paid_by', 'proof']


@admin.register(StatementSubmitted)
class StatementSubmittedAdmin(admin.ModelAdmin):
    fields = ['short_description', 'explanation', 'excursion', 'submitted']
    list_display = ['__str__', 'is_valid', 'submitted_date', 'submitted_by']
    ordering = ('-submitted_date',)
    inlines = [BillOnSubmittedStatementInline, TransactionOnSubmittedStatementInline]

    def has_add_permission(self, request, obj=None):
        return False

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = ['submitted']
        if obj is not None and obj.submitted:
            return readonly_fields + self.fields
        else:
            return readonly_fields

    def get_urls(self):
        urls = super().get_urls()

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)

            wrapper.model_admin = self
            return update_wrapper(wrapper, view)

        custom_urls = [
            path(
                "<path:object_id>/overview/",
                wrap(self.overview_view),
                name="%s_%s_overview" % (self.opts.app_label, self.opts.model_name),
            ),
            path(
                "<path:object_id>/reduce_transactions/",
                wrap(self.reduce_transactions_view),
                name="%s_%s_reduce_transactions" % (self.opts.app_label, self.opts.model_name),
            ),
        ]
        return custom_urls + urls

    def overview_view(self, request, object_id):
        statement = StatementSubmitted.objects.get(pk=object_id)
        if not statement.submitted:
            messages.error(request,
                    _("%(name)s is not yet submitted.") % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
        if "transaction_execution_confirm" in request.POST:
            res = statement.confirm(confirmer=get_member(request))
            if not res:
                # this should NOT happen!
                messages.error(request,
                        _("An error occured while trying to confirm %(name)s. Please try again.") % {'name': str(statement)})
                return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name)))

            messages.success(request,
                    _("Successfully confirmed %(name)s. I hope you executed the associated transactions, I wont remind you again.")
                    % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
        if "confirm" in request.POST:
            res = statement.validity
            if res == Statement.VALID:
                context = dict(self.admin_site.each_context(request),
                               title=_('Statement confirmed'),
                               opts=self.opts,
                               statement=statement)
                return render(request, 'admin/confirmed_statement.html', context=context)
            elif res == Statement.NON_MATCHING_TRANSACTIONS:
                messages.error(request,
                        _("Transactions do not match the covered expenses. Please correct the mistakes listed below.")
                        % {'name': str(statement)})
                return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
            elif res == Statement.MISSING_LEDGER:
                messages.error(request,
                        _("Some transactions have no ledger configured. Please fill in the gaps.")
                        % {'name': str(statement)})
                return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))

        if "reject" in request.POST:
            statement.submitted = False
            statement.save()
            messages.success(request,
                    _("Successfully rejected %(name)s. The requestor can reapply, when needed.")
                    % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))

        if "generate_transactions" in request.POST:
            if statement.transaction_set.count() > 0:
                messages.error(request,
                        _("%(name)s already has transactions. Please delete them first, if you want to generate new ones") % {'name': str(statement)})
            else:
                statement.generate_transactions()
                messages.success(request,
                        _("Successfully generated transactions for %(name)s") % {'name': str(statement)})
            return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
        context = dict(self.admin_site.each_context(request),
                       title=_('View submitted statement'),
                       opts=self.opts,
                       statement=statement,
                       transaction_issues=statement.transaction_issues,
                       total_bills=statement.total_bills,
                       total=statement.total)
        if statement.excursion is not None:
            context = dict(context,
                           nights=statement.excursion.night_count,
                           price_per_night=statement.real_night_cost,
                           duration=statement.excursion.duration,
                           staff_count=statement.real_staff_count,
                           kilometers_traveled=statement.excursion.kilometers_traveled,
                           means_of_transport=statement.excursion.get_tour_approach(),
                           euro_per_km=statement.euro_per_km,
                           allowance_per_day=settings.ALLOWANCE_PER_DAY,
                           nights_per_yl=statement.nights_per_yl,
                           allowance_per_yl=statement.allowance_per_yl,
                           transportation_per_yl=statement.transportation_per_yl,
                           total_per_yl=statement.total_per_yl,
                           total_staff=statement.total_staff)

        return render(request, 'admin/overview_submitted_statement.html', context=context)

    def reduce_transactions_view(self, request, object_id):
        statement = StatementSubmitted.objects.get(pk=object_id)
        statement.reduce_transactions()
        messages.success(request,
                _("Successfully reduced transactions for %(name)s.") % {'name': str(statement)})
        return HttpResponseRedirect(request.GET['redirectTo'])
        #return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))


@admin.register(StatementConfirmed)
class StatementConfirmedAdmin(admin.ModelAdmin):
    fields = ['short_description', 'explanation', 'excursion', 'confirmed']
    #readonly_fields = fields
    list_display = ['__str__', 'total_pretty', 'confirmed_date', 'confirmed_by']
    ordering = ('-confirmed_date',)

    def has_add_permission(self, request, obj=None):
        return False


@admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
    list_display = ['member', 'ledger', 'amount', 'reference', 'statement', 'confirmed',
            'confirmed_date', 'confirmed_by']
    list_filter = ('ledger', 'member', 'statement', 'confirmed')
    search_fields = ('reference', )
    fields = ['reference', 'amount', 'member', 'ledger', 'statement']

    def get_readonly_fields(self, request, obj=None):
        if obj is not None and obj.confirmed:
            return self.fields
        return super(TransactionAdmin, self).get_readonly_fields(request, obj)


@admin.register(Bill)
class BillAdmin(admin.ModelAdmin):
    list_display = ['__str__', 'statement', 'short_description', 'pretty_amount', 'paid_by', 'refunded']
    list_filter = ('statement', 'paid_by', 'refunded')
    search_fields = ('reference', 'statement')


def get_member(request):
    if not hasattr(request.user, 'member'):
        return None
    else:
        return request.user.member
