# -*- 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 import RequestContext
from django.template.loader import select_template
from django.utils.html import format_html
from django.utils.formats import number_format
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


class OrderPaymentInline(admin.TabularInline):
    model = OrderPayment
    extra = 0
    fields = ('amount', 'transaction_id', 'payment_method', 'created_at',)
    readonly_fields = ('created_at',)

    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_delete_permission(self, request, obj=None):
        return False


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', 'total', 'created_at',)
    list_filter = (StatusListFilter,)
    fsm_field = ('status',)
    date_hierarchy = 'created_at'
    inlines = (OrderItemInline, OrderPaymentInline,)
    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',)
    fields = ('get_number', 'status_name', ('created_at', 'updated_at'), 'get_customer_link',
              ('get_subtotal', 'get_total', 'get_outstanding_amount',),
              '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 number_format(obj.total)
    get_total.short_description = pgettext_lazy('admin', "Total")

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

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

    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


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

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = list(super(PrintOrderAdminMixin, 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_confirmation/$', self.admin_site.admin_view(self.render_confirmation),
                name='print_confirmation'),
            url(r'^(?P<pk>\d+)/print_invoice/$', self.admin_site.admin_view(self.render_invoice),
                name='print_invoice'),
        ] + super(PrintOrderAdminMixin, self).get_urls()
        return my_urls

    def _render_letter(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)
        content = template.render(RequestContext(request, {
            'customer': customer_serializer.data,
            'data': order_serializer.data,
            'order': order,
        }))
        return HttpResponse(content)

    def render_confirmation(self, request, pk=None):
        template = select_template([
            '{}/print/order-confirmation.html'.format(app_settings.APP_LABEL.lower()),
            'shop/print/order-confirmation.html'
        ])
        return self._render_letter(request, pk, template)

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

    def print_out(self, obj):
        if obj.status == 'pick_goods':
            button = reverse('admin:print_confirmation', args=(obj.id,)), pgettext_lazy('admin', "Order Confirmation")
        elif obj.status == 'pack_goods':
            button = reverse('admin:print_invoice', args=(obj.id,)), pgettext_lazy('admin', "Invoice")
        else:
            button = None
        if button:
            return format_html(
                '<span class="object-tools"><a href="{0}" class="viewsitelink" target="_new">{1}</a></span>',
                *button)
        return ''
    print_out.short_description = pgettext_lazy('admin', "Print out")


class OrderAdmin(BaseOrderAdmin):
    """
    Admin class to be used with `shop.models.defauls.order`
    """
    def get_fields(self, request, obj=None):
        fields = list(super(OrderAdmin, self).get_fields(request, obj))
        fields.extend(['shipping_address_text', 'billing_address_text'])
        return fields

    def get_search_fields(self, request):
        search_fields = list(super(OrderAdmin, self).get_search_fields(request))
        search_fields.extend(['number', 'shipping_address_text', 'billing_address_text'])
        return search_fields
