import datetime
from functools import update_wrapper

from django.contrib import admin, messages
from django.contrib.admin.helpers import AdminErrorList, AdminForm
from django.contrib.auth import get_permission_codename
from django.contrib.auth.admin import csrf_protect_m
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect, render_to_response
from django.template import RequestContext
from django.template.defaultfilters import slugify, yesno
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from tablib import Dataset

from .conf import settings
from .forms import SubmissionExportForm
from .models import Form, FormSubmission

try:
    from django.contrib.admin.utils import unquote
except ImportError:
    from django.contrib.admin.util import unquote

try:
    from django.http import JsonResponse
except ImportError:
    from .compat import JsonResponse


class FormFilter(admin.SimpleListFilter):
    title = _('Forms')
    parameter_name = 'form'

    def lookups(self, request, model_admin):
        forms = Form.active_objects.all()
        for obj in forms:
            yield (
                str(obj.id), u'%s (%s)' % (obj.name, obj.submission_count)
            )

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


class FormSubmissionAdmin(admin.ModelAdmin):
    change_form_template = 'admin/djangocms_forms/formsubmission/change_form.html'
    change_list_template = 'admin/djangocms_forms/formsubmission/change_list.html'
    export_form_template = 'admin/djangocms_forms/formsubmission/export_form.html'
    list_display = ('plugin', 'creation_date_display', 'created_by',  'ip', )
    list_filter = (FormFilter, )
    readonly_fields = ('creation_date_display', 'created_by', 'plugin', 'ip', )
    date_hierarchy = 'creation_date'
    fieldsets = (
        (None, {
            'fields': ('creation_date_display', 'created_by', 'ip', )
        }),
    )

    class Media:
        js = (
            'js/djangocms_forms/admin/jquery-form-export.js',
        )

    def has_add_permission(self, request):
        return False

    def has_export_permission(self, request):
        opts = self.opts
        codename = get_permission_codename('export', opts)
        return request.user.has_perm('%s.%s' % (opts.app_label, codename))

    def creation_date_display(self, obj):
        return obj.creation_date.strftime(settings.DJANGOCMS_FORMS_DATETIME_FORMAT)
    creation_date_display.short_description = _('Sent On')

    def get_queryset(self, request):
        qs = super(FormSubmissionAdmin, self).get_queryset(request)
        return qs.select_related('created_by', 'plugin', )

    def get_urls(self):
        """
        Add the export view to urls.
        """
        urls = super(FormSubmissionAdmin, self).get_urls()
        from django.conf.urls import patterns, url

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

        info = self.model._meta.app_label, self.model._meta.model_name

        extra_urls = patterns(
            '',
            url(r'^export/$', wrap(self.export_view), name='%s_%s_export' % info),
        )
        return extra_urls + urls

    @csrf_protect_m
    def changelist_view(self, request, extra_context=None):
        context = extra_context or {}
        context.update({
            'title': self.opts.verbose_name_plural.title(),
            'has_export_permission': self.has_export_permission(request),
        })
        return super(FormSubmissionAdmin, self).changelist_view(
            request, extra_context=context)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        context = extra_context or {}
        obj = self.get_object(request, unquote(object_id))

        if obj:
            context.update({
                'title': force_text(obj.plugin),
            })

        return super(FormSubmissionAdmin, self).change_view(
            request, object_id, form_url=form_url,
            extra_context=context)

    def export_view(self, request, form_url=''):
        """The 'export' admin view for this model."""

        info = self.opts.app_label, self.opts.model_name

        if not self.has_export_permission(request):
            raise PermissionDenied

        form = SubmissionExportForm(data=request.POST if request.method == 'POST' else None)

        if form.is_valid():
            data = form.cleaned_data
            queryset = self.get_queryset(request) \
                .filter(plugin_id=data.get('form')) \
                .select_related('created_by', 'plugin', )

            from_date, to_date = data.get('from_date'), data.get('to_date')
            headers = data.get('headers', [])

            if from_date:
                queryset = queryset.filter(creation_date__gte=from_date)
            if to_date:
                queryset = queryset.filter(creation_date__lt=to_date + datetime.timedelta(days=1))

            if not queryset.exists():
                message = _('No matching %s found for the given criteria. '
                            'Please try again.') % self.opts.verbose_name_plural
                self.message_user(request, message, level=messages.WARNING)
                if request.is_ajax():
                    data = {
                        'reloadBrowser': True,
                        'submissionCount': 0,
                    }
                    return JsonResponse(data)
                return redirect('admin:%s_%s_export' % info)

            latest_submission = queryset[:1].get()
            dataset = Dataset(title=latest_submission.plugin.name)

            if not headers:
                headers = [field['label'].strip() for field in latest_submission.form_data]
                for submission in queryset:
                    for field in submission.form_data:
                        label = field['label'].strip()
                        if label not in headers:
                            headers.append(label)

                if request.is_ajax():
                    data = {
                        'reloadBrowser': False,
                        'submissionCount': queryset.count(),
                        'availableHeaders': headers,
                    }
                    return JsonResponse(data)

            headers.extend(['Submitted on', 'Sender IP', ])
            dataset.headers = headers

            def humanize(field):
                value = field['value']
                field_type = field['type']

                if value in (None, '', [], (), {}):
                    return None

                if field_type == 'checkbox':
                    value = yesno(bool(value), u'{0},{1}'.format(_('Yes'), _('No')))
                if field_type == 'checkbox_multiple':
                    value = ', '.join(list(value))
                return value

            for submission in queryset:
                row = [None] * len(headers)
                for field in submission.form_data:
                    label = field['label'].strip()
                    if label in headers:
                        row[headers.index(label)] = humanize(field)
                    row[-2] = submission.creation_date.strftime(
                        settings.DJANGOCMS_FORMS_DATETIME_FORMAT)
                    row[-1] = submission.ip
                dataset.append(row)

            mimetype = {
                'xls': 'application/vnd.ms-excel',
                'csv': 'text/csv',
                'html': 'text/html',
                'yaml': 'text/yaml',
                'json': 'application/json',
            }

            file_type = data.get('file_type', 'xls')
            filename = settings.DJANGOCMS_FORMS_EXPORT_FILENAME.format(
                form_name=slugify(latest_submission.plugin.name))
            filename = timezone.now().strftime(filename)
            filename = '%s.%s' % (filename, file_type)

            response = HttpResponse(
                getattr(dataset, file_type), {
                    'content_type': mimetype.get(file_type, 'application/octet-stream')
                })

            response['Content-Disposition'] = 'attachment; filename=%s' % filename
            return response

        # Wrap in all admin layout
        fieldsets = ((None, {'fields': form.fields.keys()}),)
        adminform = AdminForm(form, fieldsets, {}, model_admin=self)
        media = self.media + adminform.media

        context = {
            'title': _('Export %s') % force_text(self.opts.verbose_name_plural),
            'adminform': adminform,
            'is_popup': '_popup' in request.REQUEST,
            'media': mark_safe(media),
            'errors': AdminErrorList(form, ()),
            'app_label': self.opts.app_label,
        }
        return self.render_export_from(request, context, form_url)

    def render_export_from(self, request, context, form_url=''):
        """
        Render the from submission export form.
        """
        context.update({
            'has_change_permission': self.has_change_permission(request),
            'form_url': mark_safe(form_url),
            'opts': self.opts,
            'add': True,
            'save_on_top': self.save_on_top,
        })

        context_instance = RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(
            self.export_form_template, context, context_instance=context_instance)


admin.site.register(FormSubmission, FormSubmissionAdmin)
