from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.views import generic

from .forms import AuthorForm, ConfirmDeleteForm, ContactForm
from .models import Artist, Author, Book, BookSigning, Page


class CustomTemplateView(generic.TemplateView):
    template_name = "generic_views/about.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update({"key": "value"})
        return context


class ObjectDetail(generic.DetailView):
    template_name = "generic_views/detail.html"

    def get_object(self):
        return {"foo": "bar"}


class ArtistDetail(generic.DetailView):
    queryset = Artist.objects.all()


class AuthorDetail(generic.DetailView):
    queryset = Author.objects.all()


class AuthorCustomDetail(generic.DetailView):
    template_name = "generic_views/author_detail.html"
    queryset = Author.objects.all()

    def get(self, request, *args, **kwargs):
        # Ensures get_context_object_name() doesn't reference self.object.
        author = self.get_object()
        context = {"custom_" + self.get_context_object_name(author): author}
        return self.render_to_response(context)


class PageDetail(generic.DetailView):
    queryset = Page.objects.all()
    template_name_field = "template"


class DictList(generic.ListView):
    """A ListView that doesn't use a model."""

    queryset = [{"first": "John", "last": "Lennon"}, {"first": "Yoko", "last": "Ono"}]
    template_name = "generic_views/list.html"


class ArtistList(generic.ListView):
    template_name = "generic_views/list.html"
    queryset = Artist.objects.all()


class AuthorList(generic.ListView):
    queryset = Author.objects.all()


class AuthorListGetQuerysetReturnsNone(AuthorList):
    def get_queryset(self):
        return None


class BookList(generic.ListView):
    model = Book


class CustomPaginator(Paginator):
    def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
        super().__init__(
            queryset,
            page_size,
            orphans=2,
            allow_empty_first_page=allow_empty_first_page,
        )


class AuthorListCustomPaginator(AuthorList):
    paginate_by = 5

    def get_paginator(
        self, queryset, page_size, orphans=0, allow_empty_first_page=True
    ):
        return super().get_paginator(
            queryset,
            page_size,
            orphans=2,
            allow_empty_first_page=allow_empty_first_page,
        )


class ContactView(generic.FormView):
    form_class = ContactForm
    success_url = reverse_lazy("authors_list")
    template_name = "generic_views/form.html"


class ArtistCreate(generic.CreateView):
    model = Artist
    fields = "__all__"


class NaiveAuthorCreate(generic.CreateView):
    queryset = Author.objects.all()
    fields = "__all__"


class TemplateResponseWithoutTemplate(
    generic.detail.SingleObjectTemplateResponseMixin, generic.View
):
    # we don't define the usual template_name here

    def __init__(self):
        # Dummy object, but attr is required by get_template_name()
        self.object = None


class AuthorCreate(generic.CreateView):
    model = Author
    success_url = "/list/authors/"
    fields = "__all__"


class SpecializedAuthorCreate(generic.CreateView):
    model = Author
    form_class = AuthorForm
    template_name = "generic_views/form.html"
    context_object_name = "thingy"

    def get_success_url(self):
        return reverse("author_detail", args=[self.object.id])


class AuthorCreateRestricted(AuthorCreate):
    post = method_decorator(login_required)(AuthorCreate.post)


class ArtistUpdate(generic.UpdateView):
    model = Artist
    fields = "__all__"


class NaiveAuthorUpdate(generic.UpdateView):
    queryset = Author.objects.all()
    fields = "__all__"


class AuthorUpdate(generic.UpdateView):
    get_form_called_count = 0  # Used to ensure get_form() is called once.
    model = Author
    success_url = "/list/authors/"
    fields = "__all__"

    def get_form(self, *args, **kwargs):
        self.get_form_called_count += 1
        return super().get_form(*args, **kwargs)


class OneAuthorUpdate(generic.UpdateView):
    success_url = "/list/authors/"
    fields = "__all__"

    def get_object(self):
        return Author.objects.get(pk=1)


class SpecializedAuthorUpdate(generic.UpdateView):
    model = Author
    form_class = AuthorForm
    template_name = "generic_views/form.html"
    context_object_name = "thingy"

    def get_success_url(self):
        return reverse("author_detail", args=[self.object.id])


class NaiveAuthorDelete(generic.DeleteView):
    queryset = Author.objects.all()


class AuthorDelete(generic.DeleteView):
    model = Author
    success_url = "/list/authors/"


class AuthorDeleteFormView(generic.DeleteView):
    model = Author
    form_class = ConfirmDeleteForm

    def get_success_url(self):
        return reverse("authors_list")


class SpecializedAuthorDelete(generic.DeleteView):
    queryset = Author.objects.all()
    template_name = "generic_views/confirm_delete.html"
    context_object_name = "thingy"
    success_url = reverse_lazy("authors_list")


class BookConfig:
    queryset = Book.objects.all()
    date_field = "pubdate"


class BookArchive(BookConfig, generic.ArchiveIndexView):
    pass


class BookYearArchive(BookConfig, generic.YearArchiveView):
    pass


class BookMonthArchive(BookConfig, generic.MonthArchiveView):
    pass


class BookWeekArchive(BookConfig, generic.WeekArchiveView):
    pass


class BookDayArchive(BookConfig, generic.DayArchiveView):
    pass


class BookTodayArchive(BookConfig, generic.TodayArchiveView):
    pass


class BookDetail(BookConfig, generic.DateDetailView):
    pass


class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin):
    fields = "__all__"

    def get_queryset(self):
        return Author.objects.all()


class BookDetailGetObjectCustomQueryset(BookDetail):
    def get_object(self, queryset=None):
        return super().get_object(queryset=Book.objects.filter(pk=self.kwargs["pk"]))


class CustomMultipleObjectMixinView(generic.list.MultipleObjectMixin, generic.View):
    queryset = [
        {"name": "John"},
        {"name": "Yoko"},
    ]

    def get(self, request):
        self.object_list = self.get_queryset()


class CustomContextView(generic.detail.SingleObjectMixin, generic.View):
    model = Book
    object = Book(name="dummy")

    def get_object(self):
        return Book(name="dummy")

    def get_context_data(self, **kwargs):
        context = {"custom_key": "custom_value"}
        context.update(kwargs)
        return super().get_context_data(**context)

    def get_context_object_name(self, obj):
        return "test_name"


class CustomSingleObjectView(generic.detail.SingleObjectMixin, generic.View):
    model = Book
    object = Book(name="dummy")


class BookSigningConfig:
    model = BookSigning
    date_field = "event_date"
    # use the same templates as for books

    def get_template_names(self):
        return ["generic_views/book%s.html" % self.template_name_suffix]


class BookSigningArchive(BookSigningConfig, generic.ArchiveIndexView):
    pass


class BookSigningYearArchive(BookSigningConfig, generic.YearArchiveView):
    pass


class BookSigningMonthArchive(BookSigningConfig, generic.MonthArchiveView):
    pass


class BookSigningWeekArchive(BookSigningConfig, generic.WeekArchiveView):
    pass


class BookSigningDayArchive(BookSigningConfig, generic.DayArchiveView):
    pass


class BookSigningTodayArchive(BookSigningConfig, generic.TodayArchiveView):
    pass


class BookArchiveWithoutDateField(generic.ArchiveIndexView):
    queryset = Book.objects.all()


class BookSigningDetail(BookSigningConfig, generic.DateDetailView):
    context_object_name = "book"


class NonModel:
    id = "non_model_1"

    _meta = None


class NonModelDetail(generic.DetailView):

    template_name = "generic_views/detail.html"
    model = NonModel

    def get_object(self, queryset=None):
        return NonModel()


class ObjectDoesNotExistDetail(generic.DetailView):
    def get_queryset(self):
        return Book.does_not_exist.all()


class LateValidationView(generic.FormView):
    form_class = ContactForm
    success_url = reverse_lazy("authors_list")
    template_name = "generic_views/form.html"

    def form_valid(self, form):
        form.add_error(None, "There is an error")
        return self.form_invalid(form)