from django.db import models

from modelcluster.fields import ParentalKey
from modelcluster.contrib.taggit import ClusterTaggableManager
from taggit.models import TaggedItemBase

from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index


class BlogIndexPage(Page):
    """Entry point of the blog. Displays its children `BlogPages`"""

    intro = RichTextField(blank=True)

    content_panels = Page.content_panels + [FieldPanel("intro", classname="full")]

    def get_context(self, request, *args, **kwargs):
        """
        Overrides the default context to include only published pages, ordered by reverse chronological order.
        These child pages can be accessed in the template using `blogpages` instead of page.get_children.
        """
        context = super().get_context(request)
        blogpages = self.get_children().live().order_by("-first_published_at")
        context["blogpages"] = blogpages
        return context


class BlogPageTag(TaggedItemBase):
    """Provides a simple tagging feature to Blog Pages"""

    content_object = ParentalKey(
        "BlogPage", related_name="tagged_items", on_delete=models.CASCADE
    )


class BlogPage(Page):
    """Models a blog page entry."""

    date = models.DateField("Post date")
    intro = models.CharField(max_length=250)
    body = RichTextField(blank=True)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)

    def main_image(self):
        """Returns the first image associated with a `BlogPage` from `BlogPageGalleryImage`"""
        gallery_item = self.gallery_images.first()
        if gallery_item:
            return gallery_item.image
        else:
            return None

    search_fields = Page.search_fields + [
        index.SearchField("intro"),
        index.SearchField("body"),
    ]

    content_panels = Page.content_panels + [
        # Grouping Date and tags together for readability in the Admin panel
        MultiFieldPanel(
            [FieldPanel("date"), FieldPanel("tags")], heading="Blog information"
        ),
        FieldPanel("intro"),
        FieldPanel("body", classname="full"),
        InlinePanel("gallery_images", label="Gallery images"),
    ]


class BlogPageGalleryImage(Orderable):
    """
    An image gallery descendant to a `BlogPage`

    Inherits a `sort_order` field from the `Orderable` object which keeps track of image ordering.
    The `ParentalKey` works similarly to a `ForeignKey` but also defines this class as a child of `BlogPage` model.
    This means that the image gallery associated with a particular `BlogPage` instance is treated as a part of the page
    in operations like submitting for moderation, and tracking version history.

    The `BlogPageGalleryImage.image` is a `ForeignKey to the Wagtail built-in `Image` model where images themselves are
    stored. The `ImageChooserPanel` provides a popup interface for choosing an existing image or uploading a new one.
    This allows for the same image to exist in multiple galleries - effectively creating a many-to-many relationship
    between pages and images.
    """

    page = ParentalKey(
        BlogPage, on_delete=models.CASCADE, related_name="gallery_images"
    )
    image = models.ForeignKey(
        # on_delete here means that is an image is deleted from the system, the gallery entry is deleted as well.
        "wagtailimages.Image",
        on_delete=models.CASCADE,
        related_name="+",
    )
    caption = models.CharField(blank=True, max_length=250)

    panels = [ImageChooserPanel("image"), FieldPanel("caption")]


class BlogTagIndexPage(Page):
    """
    Models the Index page of blogs by tag.

    Even though this model does not define any fields of its own, it is a subclass of Page and is added to the Wagtail
    ecosystem. This means that it can be given a title and a URL in the admin

    """

    def get_context(self, request, *args, **kwargs):
        """Filters by tage passed in request query string"""
        tag = request.GET.get('tag')
        blogpages = BlogPage.objects.filter(tags__name=tag)

        context = super().get_context(request)
        context['blogpages'] = blogpages
        return context