from crispy_forms.utils import TEMPLATE_PACK
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.db import models
from django.db.models.query import QuerySet
from django.forms.models import model_to_dict
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.utils import six
from django.utils.encoding import force_text, smart_text
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
from xadmin.layout import Field, render_field
from xadmin.plugins.inline import Inline
from xadmin.plugins.actions import BaseActionView
from xadmin.plugins.inline import InlineModelAdmin
from xadmin.sites import site
from xadmin.util import unquote, quote, model_format_dict, is_related_field2
from xadmin.views import BaseAdminPlugin, ModelAdminView, CreateAdminView, UpdateAdminView, DetailAdminView, ModelFormAdminView, DeleteAdminView, ListAdminView
from xadmin.views.base import csrf_protect_m, filter_hook
from xadmin.views.detail import DetailAdminUtil
from reversion.models import Revision, Version
from reversion.revisions import is_active, register, is_registered, set_comment, create_revision, set_user
from contextlib import contextmanager
from functools import partial


def _autoregister(admin, model, follow=None):
    """Registers a model with reversion, if required."""
    if model._meta.proxy:
        raise RegistrationError("Proxy models cannot be used with django-reversion, register the parent class instead")
    if not is_registered(model):
        follow = follow or []
        for parent_cls, field in model._meta.parents.items():
            follow.append(field.name)
            _autoregister(admin, parent_cls)
        register(model, follow=follow, format=admin.reversion_format)


def _register_model(admin, model):
    if not hasattr(admin, 'reversion_format'):
        admin.reversion_format = 'json'

    if not is_registered(model):
        inline_fields = []
        for inline in getattr(admin, 'inlines', []):
            inline_model = inline.model
            if getattr(inline, 'generic_inline', False):
                ct_field = getattr(inline, 'ct_field', 'content_type')
                ct_fk_field = getattr(inline, 'ct_fk_field', 'object_id')
                for field in model._meta.many_to_many:
                    if isinstance(field, GenericRelation) \
                            and field.rel.to == inline_model \
                            and field.object_id_field_name == ct_fk_field \
                            and field.content_type_field_name == ct_field:
                        inline_fields.append(field.name)
                _autoregister(admin, inline_model)
            else:
                fk_name = getattr(inline, 'fk_name', None)
                if not fk_name:
                    for field in inline_model._meta.fields:
                        if isinstance(field, (models.ForeignKey, models.OneToOneField)) and issubclass(model, field.rel.to):
                            fk_name = field.name
                _autoregister(admin, inline_model, follow=[fk_name])
                if not inline_model._meta.get_field(fk_name).rel.is_hidden():
                    accessor = inline_model._meta.get_field(fk_name).remote_field.get_accessor_name()
                    inline_fields.append(accessor)
        _autoregister(admin, model, inline_fields)


def register_models(admin_site=None):
    if admin_site is None:
        admin_site = site

    for model, admin in admin_site._registry.items():
        if getattr(admin, 'reversion_enable', False):
            _register_model(admin, model)

@contextmanager
def do_create_revision(request):
    with create_revision():
        set_user(request.user)
        yield

class ReversionPlugin(BaseAdminPlugin):

    # The serialization format to use when registering models with reversion.
    reversion_format = "json"

    # Whether to ignore duplicate revision data.
    ignore_duplicate_revisions = False

    reversion_enable = False

    def init_request(self, *args, **kwargs):
        return self.reversion_enable

    def do_post(self, __):
        def _method():
            self.revision_context_manager.set_user(self.user)
            comment = ''
            admin_view = self.admin_view
            if isinstance(admin_view, CreateAdminView):
                comment = _(u"Initial version.")
            elif isinstance(admin_view, UpdateAdminView):
                comment = _(u"Change version.")
            elif isinstance(admin_view, RevisionView):