import logging

from django.contrib import messages
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import FormView
from django.views.generic import ListView
from django.views.generic import RedirectView
from wiki.conf import settings as wiki_settings
from wiki.core.paginator import WikiPaginator
from wiki.decorators import get_article
from wiki.models.pluginbase import RevisionPluginRevision
from wiki.plugins.images import forms
from wiki.plugins.images import models
from wiki.views.mixins import ArticleMixin

logger = logging.getLogger(__name__)


class ImageView(ArticleMixin, ListView):
    template_name = "wiki/plugins/images/index.html"
    allow_empty = True
    context_object_name = "images"
    paginator_class = WikiPaginator
    paginate_by = 10

    @method_decorator(get_article(can_read=True, not_locked=True))
    def dispatch(self, request, article, *args, **kwargs):
        return super().dispatch(request, article, *args, **kwargs)

    def get_queryset(self):
        if self.article.can_moderate(
            self.request.user
        ) or self.article.can_delete(self.request.user):
            images = models.Image.objects.filter(article=self.article)
        else:
            images = models.Image.objects.filter(
                article=self.article, current_revision__deleted=False
            )
        images.select_related()
        return images.order_by("-current_revision__imagerevision__created")

    def get_context_data(self, **kwargs):
        kwargs.update(ArticleMixin.get_context_data(self, **kwargs))
        return ListView.get_context_data(self, **kwargs)


class DeleteView(ArticleMixin, RedirectView):
    permanent = False

    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, *args, **kwargs):
        self.image = get_object_or_404(
            models.Image, article=article, id=kwargs.get("image_id", None)
        )
        self.restore = kwargs.get("restore", False)
        return ArticleMixin.dispatch(self, request, article, *args, **kwargs)

    def get_redirect_url(self, **kwargs):
        if not self.image.current_revision:
            logger.critical(
                "Encountered an image without current revision set, ID: {}".format(
                    self.image.id
                )
            )
            latest_revision = RevisionPluginRevision.objects.filter(
                plugin=self.image
            ).latest("pk")
            self.image.current_revision = latest_revision

        new_revision = models.ImageRevision()
        new_revision.inherit_predecessor(self.image)
        new_revision.set_from_request(self.request)
        new_revision.revision_number = RevisionPluginRevision.objects.filter(
            plugin=self.image
        ).count()
        new_revision.deleted = not self.restore
        new_revision.save()
        self.image.current_revision = new_revision
        self.image.save()
        if self.restore:
            messages.info(
                self.request,
                _("%s has been restored") % new_revision.get_filename(),
            )
        else:
            messages.info(
                self.request,
                _("%s has been marked as deleted")
                % new_revision.get_filename(),
            )
        if self.urlpath:
            return reverse(
                "wiki:images_index", kwargs={"path": self.urlpath.path}
            )
        return reverse(
            "wiki:images_index", kwargs={"article_id": self.article.id}
        )


class PurgeView(ArticleMixin, FormView):
    template_name = "wiki/plugins/images/purge.html"
    permanent = False
    form_class = forms.PurgeForm

    @method_decorator(get_article(can_write=True, can_moderate=True))
    def dispatch(self, request, article, *args, **kwargs):
        self.image = get_object_or_404(
            models.Image, article=article, id=kwargs.get("image_id", None)
        )
        return super().dispatch(request, article, *args, **kwargs)

    def form_valid(self, form):
        for revision in self.image.revision_set.all().select_related(
            "imagerevision"
        ):
            revision.imagerevision.image.delete(save=False)
            revision.imagerevision.delete()

        if self.urlpath:
            return redirect("wiki:images_index", path=self.urlpath.path)
        return redirect("wiki:images_index", article_id=self.article_id)

    def get_context_data(self, **kwargs):
        # Needed since Django 1.9 because get_context_data is no longer called
        # with the form instance
        if "form" not in kwargs:
            kwargs["form"] = self.get_form()
        kwargs = ArticleMixin.get_context_data(self, **kwargs)
        kwargs.update(FormView.get_context_data(self, **kwargs))
        return kwargs


class RevisionChangeView(ArticleMixin, RedirectView):
    permanent = False

    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, *args, **kwargs):
        self.image = get_object_or_404(
            models.Image, article=article, id=kwargs.get("image_id", None)
        )
        self.revision = get_object_or_404(
            models.ImageRevision,
            plugin__article=article,
            id=kwargs.get("rev_id", None),
        )
        return ArticleMixin.dispatch(self, request, article, *args, **kwargs)

    def get_redirect_url(self, **kwargs):
        self.image.current_revision = self.revision
        self.image.save()
        messages.info(
            self.request,
            _("%(file)s has been changed to revision #%(revision)d")
            % {
                "file": self.image.current_revision.imagerevision.get_filename(),
                "revision": self.revision.revision_number,
            },
        )
        if self.urlpath:
            return reverse(
                "wiki:images_index", kwargs={"path": self.urlpath.path}
            )
        return reverse(
            "wiki:images_index", kwargs={"article_id": self.article.id}
        )


class RevisionAddView(ArticleMixin, FormView):
    template_name = "wiki/plugins/images/revision_add.html"
    form_class = forms.RevisionForm

    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, *args, **kwargs):
        self.image = get_object_or_404(
            models.Image, article=article, id=kwargs.get("image_id", None)
        )
        if not self.image.can_write(request.user):
            return redirect(wiki_settings.LOGIN_URL)
        return ArticleMixin.dispatch(self, request, article, *args, **kwargs)

    def get_form_kwargs(self, **kwargs):
        kwargs = super().get_form_kwargs(**kwargs)
        kwargs["image"] = self.image
        kwargs["request"] = self.request
        return kwargs

    def get_context_data(self, **kwargs):
        # Needed since Django 1.9 because get_context_data is no longer called
        # with the form instance
        if "form" not in kwargs:
            kwargs["form"] = self.get_form()
        kwargs = super().get_context_data(**kwargs)
        kwargs["image"] = self.image
        return kwargs

    def form_valid(self, form, **kwargs):
        form.save()
        messages.info(
            self.request,
            _("%(file)s has been saved.")
            % {
                "file": self.image.current_revision.imagerevision.get_filename()
            },
        )
        if self.urlpath:
            return redirect("wiki:edit", path=self.urlpath.path)
        return redirect("wiki:edit", article_id=self.article.id)
