from django import forms
from django.conf.urls import patterns, url
from django.contrib import admin, messages
from django.contrib.admin.util import unquote
from django.contrib.auth import admin as auth
from django.http import HttpResponseRedirect
from django.utils.safestring import mark_safe
from django.utils.six.moves.urllib.parse import parse_qsl
from django.utils.translation import ugettext_lazy as _

from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url
from orchestra.core import services, accounts

from .filters import HasMainUserListFilter
from .forms import AccountCreationForm, AccountChangeForm
from .models import Account


class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin):
    list_display = ('username', 'type', 'is_active')
    list_filter = (
        'type', 'is_active', HasMainUserListFilter
    )
    add_fieldsets = (
        (_("User"), {
            'fields': ('username', 'password1', 'password2',),
        }),
        (_("Personal info"), {
            'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'),
        }),
        (_("Permissions"), {
            'fields': ('is_superuser', 'is_active')
        }),
        (_("Important dates"), {
            'fields': ('last_login', 'date_joined')
        }),
    )
    fieldsets = (
        (_("User"), {
            'fields': ('username', 'password',)
        }),
        (_("Personal info"), {
            'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'),
        }),
        (_("Permissions"), {
            'fields': ('is_superuser', 'is_active')
        }),
        (_("Important dates"), {
            'fields': ('last_login', 'date_joined')
        }),
    )
    search_fields = ('username',)
    add_form = AccountCreationForm
    form = AccountChangeForm
    filter_horizontal = ()
    change_form_template = 'admin/accounts/account/change_form.html'
    
    def formfield_for_dbfield(self, db_field, **kwargs):
        """ Make value input widget bigger """
        if db_field.name == 'comments':
            kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
        return super(AccountAdmin, self).formfield_for_dbfield(db_field, **kwargs)
    
    def change_view(self, request, object_id, form_url='', extra_context=None):
        if request.method == 'GET':
            account = self.get_object(request, unquote(object_id))
            if not account.is_active:
                messages.warning(request, 'This account is disabled.')
        context = {
            'services': sorted(
                [ model._meta for model in services.get() if model is not Account ],
                key=lambda i: i.verbose_name_plural.lower()
            ),
            'accounts': sorted(
                [ model._meta for model in accounts.get() if model is not Account ],
                key=lambda i: i.verbose_name_plural.lower()
            )
        }
        context.update(extra_context or {})
        return super(AccountAdmin, self).change_view(request, object_id,
                form_url=form_url, extra_context=context)
    
    def get_queryset(self, request):
        """ Select related for performance """
        qs = super(AccountAdmin, self).get_queryset(request)
        related = ('invoicecontact',)
        return qs.select_related(*related)


admin.site.register(Account, AccountAdmin)


class AccountListAdmin(AccountAdmin):
    """ Account list to allow account selection when creating new services """
    list_display = ('select_account', 'type', 'username')
    actions = None
    search_fields = ['username',]
    ordering = ('username',)
    
    def select_account(self, instance):
        # TODO get query string from request.META['QUERY_STRING'] to preserve filters
        context = {
            'url': '../?account=' + str(instance.pk),
            'name': instance.username
        }
        return '<a href="%(url)s">%(name)s</a>' % context
    select_account.short_description = _("account")
    select_account.allow_tags = True
    select_account.order_admin_field = 'username'
    
    def changelist_view(self, request, extra_context=None):
        original_app_label = request.META['PATH_INFO'].split('/')[-5]
        original_model = request.META['PATH_INFO'].split('/')[-4]
        context = {
            'title': _("Select account for adding a new %s") % (original_model),
            'original_app_label': original_app_label,
            'original_model': original_model,
        }
        context.update(extra_context or {})
        return super(AccountListAdmin, self).changelist_view(request,
                extra_context=context)


class AccountAdminMixin(object):
    """ Provide basic account support to ModelAdmin and AdminInline classes """
    readonly_fields = ('account_link',)
    filter_by_account_fields = []
    change_list_template = 'admin/accounts/account/change_list.html'
    change_form_template = 'admin/accounts/account/change_form.html'
    
    def account_link(self, instance):
        account = instance.account if instance.pk else self.account
        url = change_url(account)
        return '<a href="%s">%s</a>' % (url, str(account))
    account_link.short_description = _("account")
    account_link.allow_tags = True
    account_link.admin_order_field = 'account__username'
    
    def get_readonly_fields(self, request, obj=None):
        """ provide account for filter_by_account_fields """
        if obj:
            self.account = obj.account
        return super(AccountAdminMixin, self).get_readonly_fields(request, obj=obj)
    
    def get_queryset(self, request):
        """ Select related for performance """
        qs = super(AccountAdminMixin, self).get_queryset(request)
        return qs.select_related('account')
    
    def formfield_for_dbfield(self, db_field, **kwargs):
        """ Filter by account """
        formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs)
        if db_field.name in self.filter_by_account_fields:
            if hasattr(self, 'account'):
                # Hack widget render in order to append ?account=id to the add url
                old_render = formfield.widget.render
                def render(*args, **kwargs):
                    output = old_render(*args, **kwargs)
                    output = output.replace('/add/"', '/add/?account=%s"' % self.account.pk)
                    return mark_safe(output)
                formfield.widget.render = render
                # Filter related object by account
                formfield.queryset = formfield.queryset.filter(account=self.account)
        return formfield
    
    def get_account_from_preserve_filters(self, request):
        preserved_filters = self.get_preserved_filters(request)
        preserved_filters = dict(parse_qsl(preserved_filters))
        cl_filters = preserved_filters.get('_changelist_filters')
        if cl_filters:
            return dict(parse_qsl(cl_filters)).get('account')
    
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        account_id = self.get_account_from_preserve_filters(request)
        if not object_id:
            if account_id:
                # Preselect account
                set_url_query(request, 'account', account_id)
        context = {
            'from_account': bool(account_id),
            'account': not account_id or Account.objects.get(pk=account_id),
            'account_opts': Account._meta,
        }
        context.update(extra_context or {})
        return super(AccountAdminMixin, self).changeform_view(request,
                object_id=object_id, form_url=form_url, extra_context=context)
    
    def changelist_view(self, request, extra_context=None):
        account_id = request.GET.get('account')
        context = {}
        if account_id:
            opts = self.model._meta
            account = Account.objects.get(pk=account_id)
            context = {
                'account': not account_id or Account.objects.get(pk=account_id),
                'account_opts': Account._meta,
                'all_selected': True,
            }
            if not request.GET.get('all'):
                context.update({
                    'all_selected': False,
                    'title': _("Select %s to change for %s") % (
                        opts.verbose_name, account.username),
                })
            else:
                request_copy = request.GET.copy()
                request_copy.pop('account')
                request.GET = request_copy
        context.update(extra_context or {})
        return super(AccountAdminMixin, self).changelist_view(request,
                extra_context=context)


class SelectAccountAdminMixin(AccountAdminMixin):
    """ Provides support for accounts on ModelAdmin """
    def get_inline_instances(self, request, obj=None):
        inlines = super(AccountAdminMixin, self).get_inline_instances(request, obj=obj)
        if hasattr(self, 'account'):
            account = self.account
        else:
            account = Account.objects.get(pk=request.GET['account'])
        [ setattr(inline, 'account', account) for inline in inlines ]
        return inlines
    
    def get_urls(self):
        """ Hooks select account url """
        urls = super(AccountAdminMixin, self).get_urls()
        admin_site = self.admin_site
        opts = self.model._meta
        info = opts.app_label, opts.model_name
        account_list = AccountListAdmin(Account, admin_site).changelist_view
        select_urls = patterns("",
            url("/select-account/$",
                wrap_admin_view(self, account_list),
                name='%s_%s_select_account' % info),
        )
        return select_urls + urls 
    
    def add_view(self, request, form_url='', extra_context=None):
        """ Redirects to select account view if required """
        if request.user.is_superuser:
            from_account_id = self.get_account_from_preserve_filters(request)
            if from_account_id:
                set_url_query(request, 'account', from_account_id)
            account_id = request.GET.get('account')
            if account_id or Account.objects.count() == 1:
                kwargs = {}
                if account_id:
                    kwargs = dict(pk=account_id)
                self.account = Account.objects.get(**kwargs)
                opts = self.model._meta
                context = {
                    'title': _("Add %s for %s") % (opts.verbose_name, self.account.username),
                    'from_account': bool(from_account_id),
                    'account': self.account,
                    'account_opts': Account._meta,
                }
                context.update(extra_context or {})
                return super(AccountAdminMixin, self).add_view(request,
                        form_url=form_url, extra_context=context)
        return HttpResponseRedirect('./select-account/?%s' % request.META['QUERY_STRING'])
    
    def save_model(self, request, obj, form, change):
        """
        Given a model instance save it to the database.
        """
        if not change:
            obj.account_id = self.account.pk
        obj.save()
