
from django.template import loader
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.db.models import Q
from django.forms import ModelChoiceField
from django.http import QueryDict

from xadmin.plugins.utils import get_context_dict
from xadmin.sites import site
from xadmin.views import ModelAdminView, BaseAdminPlugin, ListAdminView
from xadmin.views.list import COL_LIST_VAR, ORDER_VAR
from xadmin.views.dashboard import widget_manager, BaseWidget, PartialBaseWidget
from xadmin.filters import FILTER_PREFIX, SEARCH_VAR
from xadmin.plugins.relate import RELATE_PREFIX

from xadmin.models import Bookmark

csrf_protect_m = method_decorator(csrf_protect)


class BookmarkPlugin(BaseAdminPlugin):

    # [{'title': "Female", 'query': {'gender': True}, 'order': ('-age'), 'cols': ('first_name', 'age', 'phones'), 'search': 'Tom'}]
    list_bookmarks = []
    show_bookmarks = True

    def has_change_permission(self, obj=None):
        if not obj or self.user.is_superuser:
            return True
        else:
            return obj.user == self.user

    def get_context(self, context):
        if not self.show_bookmarks:
            return context

        bookmarks = []

        current_qs = '&'.join(['%s=%s' % (k, v) for k, v in sorted(
            filter(lambda i: bool(i[1] and (i[0] in (COL_LIST_VAR, ORDER_VAR, SEARCH_VAR) or i[0].startswith(FILTER_PREFIX)
                                            or i[0].startswith(RELATE_PREFIX))), self.request.GET.items()))])

        model_info = (self.opts.app_label, self.opts.model_name)
        has_selected = False
        menu_title = _(u"Bookmark")
        list_base_url = reverse('xadmin:%s_%s_changelist' %
                                model_info, current_app=self.admin_site.name)

        # local bookmarks
        for bk in self.list_bookmarks:
            title = bk['title']
            params = dict(
                [(FILTER_PREFIX + k, v) for (k, v) in bk['query'].items()])
            if 'order' in bk:
                params[ORDER_VAR] = '.'.join(bk['order'])
            if 'cols' in bk:
                params[COL_LIST_VAR] = '.'.join(bk['cols'])
            if 'search' in bk:
                params[SEARCH_VAR] = bk['search']
            def check_item(i):
                return bool(i[1]) or i[1] == False
            bk_qs = '&'.join(['%s=%s' % (k, v) for k, v in sorted(filter(check_item, params.items()))])

            url = list_base_url + '?' + bk_qs
            selected = (current_qs == bk_qs)

            bookmarks.append(
                {'title': title, 'selected': selected, 'url': url})
            if selected:
                menu_title = title
                has_selected = True

        content_type = ContentType.objects.get_for_model(self.model)
        bk_model_info = (Bookmark._meta.app_label, Bookmark._meta.model_name)
        bookmarks_queryset = Bookmark.objects.filter(
            content_type=content_type,
            url_name='xadmin:%s_%s_changelist' % model_info
        ).filter(Q(user=self.user) | Q(is_share=True))

        for bk in bookmarks_queryset:
            selected = (current_qs == bk.query)

            if self.has_change_permission(bk):
                change_or_detail = 'change'
            else:
                change_or_detail = 'detail'

            bookmarks.append({'title': bk.title, 'selected': selected, 'url': bk.url, 'edit_url':
                              reverse('xadmin:%s_%s_%s' % (bk_model_info[0], bk_model_info[1], change_or_detail),
                                      args=(bk.id,))})
            if selected:
                menu_title = bk.title
                has_selected = True

        post_url = reverse('xadmin:%s_%s_bookmark' % model_info,
                           current_app=self.admin_site.name)

        new_context = {
            'bk_menu_title': menu_title,
            'bk_bookmarks': bookmarks,
            'bk_current_qs': current_qs,
            'bk_has_selected': has_selected,
            'bk_list_base_url': list_base_url,
            'bk_post_url': post_url,
            'has_add_permission_bookmark': self.admin_view.request.user.has_perm('xadmin.add_bookmark'),
            'has_change_permission_bookmark': self.admin_view.request.user.has_perm('xadmin.change_bookmark')
        }
        context.update(new_context)
        return context

    # Media
    def get_media(self, media):
        return media + self.vendor('xadmin.plugin.bookmark.js')

    # Block Views
    def block_nav_menu(self, context, nodes):
        if self.show_bookmarks:
            nodes.insert(0, loader.render_to_string('xadmin/blocks/model_list.nav_menu.bookmarks.html',
                                                    context=get_context_dict(context)))


class BookmarkView(ModelAdminView):

    @csrf_protect_m
    @transaction.atomic
    def post(self, request):
        model_info = (self.opts.app_label, self.opts.model_name)
        url_name = 'xadmin:%s_%s_changelist' % model_info
        bookmark = Bookmark(
            content_type=ContentType.objects.get_for_model(self.model),
            title=request.POST[
                'title'], user=self.user, query=request.POST.get('query', ''),
            is_share=request.POST.get('is_share', 0), url_name=url_name)
        bookmark.save()
        content = {'title': bookmark.title, 'url': bookmark.url}
        return self.render_response(content)


class BookmarkAdmin(object):

    model_icon = 'fa fa-book'
    list_display = ('title', 'user', 'url_name', 'query')
    list_display_links = ('title',)
    user_fields = ['user']
    hidden_menu = True

    def queryset(self):
        if self.user.is_superuser:
            return Bookmark.objects.all()
        return Bookmark.objects.filter(Q(user=self.user) | Q(is_share=True))

    def get_list_display(self):
        list_display = super(BookmarkAdmin, self).get_list_display()
        if not self.user.is_superuser:
            list_display.remove('user')
        return list_display


    def has_change_permission(self, obj=None):
        if not obj or self.user.is_superuser:
            return True
        else:
            return obj.user == self.user


@widget_manager.register
class BookmarkWidget(PartialBaseWidget):
    widget_type = _('bookmark')
    widget_icon = 'fa fa-bookmark'
    description = _(
        'Bookmark Widget, can show user\'s bookmark list data in widget.')
    template = "xadmin/widgets/list.html"

    bookmark = ModelChoiceField(
        label=_('Bookmark'), queryset=Bookmark.objects.all(), required=False)

    def setup(self):
        BaseWidget.setup(self)

        bookmark = self.cleaned_data['bookmark']
        model = bookmark.content_type.model_class()
        data = QueryDict(bookmark.query)
        self.bookmark = bookmark

        if not self.title:
            self.title = unicode(bookmark)

        req = self.make_get_request("", data.items())
        self.list_view = self.get_view_class(
            ListAdminView, model, list_per_page=10, list_editable=[])(req)

    def has_perm(self):
        return True

    def context(self, context):
        list_view = self.list_view
        list_view.make_result_list()

        base_fields = list_view.base_list_display
        if len(base_fields) > 5:
            base_fields = base_fields[0:5]

        context['result_headers'] = [c for c in list_view.result_headers(
        ).cells if c.field_name in base_fields]
        context['results'] = [[o for i, o in
                               enumerate(filter(lambda c:c.field_name in base_fields, r.cells))]
                              for r in list_view.results()]
        context['result_count'] = list_view.result_count
        context['page_url'] = self.bookmark.url

site.register(Bookmark, BookmarkAdmin)
site.register_plugin(BookmarkPlugin, ListAdminView)
site.register_modelview(r'^bookmark/$', BookmarkView, name='%s_%s_bookmark')
