# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf.urls import url
from django.contrib import admin
from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.models.fields import Field, FieldDoesNotExist
from django.forms import widgets
from django.http import HttpResponse
from django.template.loader import select_template
from django.utils.html import format_html
from django.utils.translation import pgettext_lazy
from fsm_admin.mixins import FSMTransitionMixin
from shop.conf import app_settings
from shop.models.customer import CustomerModel
from shop.models.order import OrderItemModel, OrderPayment
from shop.modifiers.pool import cart_modifiers_pool
from shop.serializers.order import OrderDetailSerializer
from shop.transition import transition_change_notification


class OrderPaymentInline(admin.TabularInline):
    model = OrderPayment
    extra = 0

    def get_formset(self, request, obj=None, **kwargs):
        """
        Convert the field `payment_method` into a select box with all possible payment methods.
        """
        choices = [pm.get_choice() for pm in cart_modifiers_pool.get_payment_modifiers()]
        kwargs.update(widgets={'payment_method': widgets.Select(choices=choices)})
        formset = super(OrderPaymentInline, self).get_formset(request, obj, **kwargs)
        return formset

    def has_add_permission(self, request, obj=None):
        assert obj is not None, "An Order object can not be added through the Django-Admin"
        return obj.status in ['awaiting_payment', 'refund_payment']

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

    def get_max_num(self, request, obj=None, **kwargs):
        qs = self.model.objects.filter(order=obj)
        if self.has_add_permission(request, obj):
            return qs.count() + 1
        return qs.count()

    def get_fields(self, request, obj=None):
        if self.has_add_permission(request, obj):
            return ['amount', 'transaction_id', 'payment_method', 'created_at']
        return ['get_amount', 'transaction_id', 'payment_method', 'created_at']

    def get_readonly_fields(self, request, obj=None):
        if self.has_add_permission(request, obj):
            return ['created_at', 'get_amount']
        # unless we expect a payment, set all fields as readonly
        return self.get_fields(request, obj)

    def get_amount(self, obj):
        """Return amount using correct local format"""
        return obj.amount
    get_amount.short_description = pgettext_lazy('admin', "Amount Paid")


class OrderItemInline(admin.StackedInline):
    model = OrderItemModel
    extra = 0
    fields = [
        ('product_code', 'unit_price', 'line_total',),
        ('quantity',),
        'render_as_html_extra',
    ]
    readonly_fields = ['product_code', 'quantity', 'unit_price', 'line_total', 'render_as_html_extra']
    template = 'shop/admin/edit_inline/stacked-order.html'

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

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

    def get_max_num(self, request, obj=None, **kwargs):
        return self.model.objects.filter(order=obj).count()

    def render_as_html_extra(self, obj):
        item_extra_template = select_template([
            '{0}/admin/orderitem-{1}-extra.html'.format(app_settings.APP_LABEL, obj.product.product_model),
            '{0}/admin/orderitem-product-extra.html'.format(app_settings.APP_LABEL),
            'shop/admin/orderitem-product-extra.html',
        ])
        return item_extra_template.render(obj.extra)
    render_as_html_extra.short_description = pgettext_lazy('admin', "Extra data")


class StatusListFilter(admin.SimpleListFilter):
    title = pgettext_lazy('admin', "Status")
    parameter_name = 'status'

    def lookups(self, request, model_admin):
        lookups = dict(model_admin.model._transition_targets)
        lookups.pop('new')
        lookups.pop('created')
        return lookups.items()

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(status=self.value())
        return queryset


class BaseOrderAdmin(FSMTransitionMixin, admin.ModelAdmin):
    list_display = ['get_number', 'customer', 'status_name', 'get_total', 'created_at']
    list_filter = [StatusListFilter]
    fsm_field = ['status']
    date_hierarchy = 'created_at'
    inlines = [OrderItemInline]
    readonly_fields = ['get_number', 'status_name', 'get_total', 'get_subtotal',
                       'get_customer_link', 'get_outstanding_amount', 'created_at', 'updated_at',
                       'render_as_html_extra', 'stored_request', 'is_fully_paid']
    fields = ['get_number', 'status_name',
              ('created_at', 'updated_at'),
              'get_customer_link',
              ('get_subtotal', 'get_total', 'get_outstanding_amount', 'is_fully_paid'),
              'render_as_html_extra', 'stored_request']
    actions = None
    change_form_template = 'shop/admin/change_form.html'

    def __init__(self, *args, **kwargs):
        super(BaseOrderAdmin, self).__init__(*args, **kwargs)
        self.extra_template = select_template([
            '{}/admin/order-extra.html'.format(app_settings.APP_LABEL),
            'shop/admin/order-extra.html',
        ])

    def get_number(self, obj):
        return obj.get_number()
    get_number.short_description = pgettext_lazy('admin', "Order number")

    def get_total(self, obj):
        return str(obj.total)
    get_total.short_description = pgettext_lazy('admin', "Total")

    def get_subtotal(self, obj):
        return str(obj.subtotal)
    get_subtotal.short_description = pgettext_lazy('admin', "Subtotal")

    def get_outstanding_amount(self, obj):
        return str(obj.outstanding_amount)
    get_outstanding_amount.short_description = pgettext_lazy('admin', "Outstanding amount")

    def is_fully_paid(self, obj):
        return obj.is_fully_paid()
    is_fully_paid.short_description = pgettext_lazy('admin', "Is fully paid")
    is_fully_paid.boolean = True

    def has_add_permission(self, request):
        return False

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

    def render_as_html_extra(self, obj):
        return self.extra_template.render(obj.extra)
    render_as_html_extra.short_description = pgettext_lazy('admin', "Extra data")

    def get_customer_link(self, obj):
        try:
            url = reverse('admin:shop_customerproxy_change', args=(obj.customer.pk,))
            return format_html('<a href="{0}" target="_new">{1}</a>', url, obj.customer.get_username())
        except NoReverseMatch:
            return format_html('<strong>{0}</strong>', obj.customer.get_username())
    get_customer_link.short_description = pgettext_lazy('admin', "Customer")

    def get_search_fields(self, request):
        search_fields = list(super(BaseOrderAdmin, self).get_search_fields(request))
        search_fields.extend(['customer__user__email', 'customer__user__last_name'])
        try:
            # if CustomerModel contains a number field, let search for it
            if isinstance(CustomerModel._meta.get_field('number'), Field):
                search_fields.append('customer__number')
        except FieldDoesNotExist:
            pass
        return search_fields

    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(BaseOrderAdmin, self).get_form(request, obj, **kwargs)
        if obj:
            # store the requested transition inside the instance, so that the model's `clean()` method can access it
            obj._fsm_requested_transition = self._get_requested_transition(request)
        return ModelForm

    def change_view(self, request, object_id, form_url='', extra_context=None):
        assert object_id, "An Order object can not be added through the Django-Admin"
        current_status = self.get_object(request, object_id).status
        response = super(BaseOrderAdmin, self).change_view(request, object_id, form_url, extra_context)
        order = self.get_object(request, object_id)
        if current_status != order.status:
            transition_change_notification(order)
        return response


class PrintInvoiceAdminMixin(object):
    """
    A customized OrderAdmin class shall inherit from this mixin class, to add
    methods for printing the the invoice.
    """
    def get_fields(self, request, obj=None):
        fields = list(super(PrintInvoiceAdminMixin, self).get_fields(request, obj))
        fields.append('print_out')
        return fields

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = list(super(PrintInvoiceAdminMixin, self).get_readonly_fields(request, obj))
        readonly_fields.append('print_out')
        return readonly_fields

    def get_urls(self):
        my_urls = [
            url(r'^(?P<pk>\d+)/print_invoice/$', self.admin_site.admin_view(self.render_invoice),
                name='print_invoice'),
        ]
        my_urls.extend(super(PrintInvoiceAdminMixin, self).get_urls())
        return my_urls

    def _render_content(self, request, pk, template):
        order = self.get_object(request, pk)
        context = {'request': request, 'render_label': 'print'}
        customer_serializer = app_settings.CUSTOMER_SERIALIZER(order.customer)
        order_serializer = OrderDetailSerializer(order, context=context)
        return template.render(context={
            'customer': customer_serializer.data,
            'order': order_serializer.data,
            'object': order,
        }, request=request)

    def render_invoice(self, request, pk=None):
        template = select_template([
            '{}/print/invoice.html'.format(app_settings.APP_LABEL.lower()),
            'shop/print/invoice.html'
        ])
        content = self._render_content(request, pk, template)
        return HttpResponse(content)

    def print_out(self, obj):
        if obj.status in ['ready_for_delivery']:
            link = reverse('admin:print_invoice', args=(obj.id,)), pgettext_lazy('admin', "Invoice")
            return format_html(
                '<span class="object-tools"><a href="{0}" class="viewsitelink" target="_new">{1}</a></span>',
                *link)
        return ''
    print_out.short_description = pgettext_lazy('admin', "Print out")
