import datetime
import json
from urllib.parse import urlencode

from django.db import models
from django.utils.html import strip_tags
from django.utils.translation import gettext_lazy as _

# pylint: disable=E1101, E0401
from rr.edit_handlers import RoadRunnerPanel
from rr.fields import RoadRunnerField

from wagtail import blocks
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.images import get_image_model_string
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.widgets import AdminImageChooser
from wagtail.search import index

from ocyan.core.fender import config
from ocyan.main.loading import get_ocyan_classes
from ocyan.plugin.wagtail.blocks import WrappedCharBlock
from ocyan.plugin.wagtail.models import AbstractBasePage

# override points for project-specific behavior
parse_items_per_page, paginate_queryset, recent_from_queryset = get_ocyan_classes(
    "ocyan_plugin_wagtail_blog.utils",
    ["parse_items_per_page", "paginate_queryset", "recent_from_queryset"],
)


BLOG_TEMPLATE_STYLE_DEFAULT = "default"
BLOG_TEMPLATE_STYLE_PREMIUM = "premium"

BLOG_TEMPLATE_STYLE_CHOICES = [
    (BLOG_TEMPLATE_STYLE_DEFAULT, _("Default")),
    (BLOG_TEMPLATE_STYLE_PREMIUM, _("Premium")),
]


class BlogPageList(AbstractBasePage):
    subpage_types = [
        "ocyan_plugin_wagtail_blog.BlogPage",
        "ocyan_plugin_wagtail_blog.PageMakerBlogPage",
    ]
    template = "blog/blog_list.html"
    default_list_template = "blog/blog_list.html"
    premium_list_template = "blog/packs/premium/blog_list.html"
    default_detail_template = "blog/blog_item.html"
    premium_detail_template = "blog/packs/premium/blog_item.html"
    default_pagemaker_detail_template = "blog/pagemaker_blog_item.html"
    premium_pagemaker_detail_template = "blog/packs/premium/pagemaker_blog_item.html"

    template_style = models.CharField(
        max_length=20,
        choices=BLOG_TEMPLATE_STYLE_CHOICES,
        default=BLOG_TEMPLATE_STYLE_DEFAULT,
        verbose_name=_("Template style"),
    )

    content_panels = AbstractBasePage.content_panels + [
        MultiFieldPanel(
            [FieldPanel("template_style")],
            heading=_("Presentation"),
            help_text=_(
                "Choose the blog template pack. Projects and themes can override each pack separately."
            ),
        ),
    ]

    def get_template_style(self):
        valid_styles = {choice[0] for choice in BLOG_TEMPLATE_STYLE_CHOICES}
        if self.template_style in valid_styles:
            return self.template_style
        return BLOG_TEMPLATE_STYLE_DEFAULT

    def get_list_template_name(self):
        if self.get_template_style() == BLOG_TEMPLATE_STYLE_PREMIUM:
            return self.premium_list_template
        return self.default_list_template

    def get_detail_template_name(self):
        if self.get_template_style() == BLOG_TEMPLATE_STYLE_PREMIUM:
            return self.premium_detail_template
        return self.default_detail_template

    def get_pagemaker_detail_template_name(self):
        if self.get_template_style() == BLOG_TEMPLATE_STYLE_PREMIUM:
            return self.premium_pagemaker_detail_template
        return self.default_pagemaker_detail_template

    def get_stylesheet_paths(self):
        stylesheet_paths = ["wagtail_blog/css/blog.css"]
        if self.get_template_style() == BLOG_TEMPLATE_STYLE_PREMIUM:
            stylesheet_paths.append("wagtail_blog/css/blog-premium.css")
        return stylesheet_paths

    def get_template(self, request, *args, **kwargs):
        return self.get_list_template_name()

    def get_base_blog_queryset(self):
        return BlogPage.objects.child_of(self).live()

    def apply_locale_filter(self, queryset):
        locale_id = getattr(self, "locale_id", None)
        if locale_id:
            return queryset.filter(locale_id=locale_id)
        return queryset

    def order_blog_queryset(self, queryset):
        return queryset.order_by("-post_date")

    def get_blog_queryset(self):
        return self.order_blog_queryset(
            self.apply_locale_filter(self.get_base_blog_queryset())
        )

    def get_items_per_page(self):
        return parse_items_per_page(config.get("wagtail_blog", "items_per_page", 10))

    def get_meta_description(self):
        return self.search_description or None

    def get_structured_data(self):
        structured_data = {
            "@context": "https://schema.org",
            "@type": "CollectionPage",
            "name": self.get_meta_title(),
            "url": self.get_meta_url(),
        }
        description = self.get_meta_description()
        if description:
            structured_data["description"] = description
        return structured_data

    def get_structured_data_json(self):
        return json.dumps(self.get_structured_data())

    def get_canonical_url(self, request=None):
        base_url = self.get_meta_url()
        if request is None or not hasattr(request, "GET"):
            return base_url

        page_number = request.GET.get("p")
        if not page_number or str(page_number) == "1":
            return base_url

        return f"{base_url}?{urlencode({'p': page_number})}"

    def get_prev_page_url(self, request, page_obj):
        if page_obj.number <= 1:
            return None

        previous_page = page_obj.previous_page_number()
        if previous_page == 1:
            return self.get_meta_url()

        return f"{self.get_meta_url()}?{urlencode({'p': previous_page})}"

    def get_next_page_url(self, request, page_obj):
        if not page_obj.has_next():
            return None

        next_page = page_obj.next_page_number()
        return f"{self.get_meta_url()}?{urlencode({'p': next_page})}"

    def get_paginated_blogs(self, request):
        blog_queryset = self.get_blog_queryset()
        paginator, page_obj = paginate_queryset(
            blog_queryset,
            request.GET.get("p", 1),
            self.get_items_per_page(),
        )
        return blog_queryset, paginator, page_obj

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        all_blogs, paginator, page_obj = self.get_paginated_blogs(request)

        context["page"] = self
        context["bloglist"] = self.get_siblings()
        context["all_blogs"] = all_blogs
        context["blogs"] = page_obj.object_list
        context["pages"] = page_obj.object_list
        context["page_obj"] = page_obj
        context["current_page"] = page_obj.number
        context["last_page"] = paginator.num_pages
        context["paginator"] = paginator
        context["canonical_url"] = self.get_canonical_url(request)
        context["prev_page_url"] = self.get_prev_page_url(request, page_obj)
        context["next_page_url"] = self.get_next_page_url(request, page_obj)
        context["blog_template_style"] = self.get_template_style()
        context["blog_stylesheet_paths"] = self.get_stylesheet_paths()

        return context

    class Meta:
        verbose_name = _("Blog list")
        verbose_name_plural = _("Blog lists")


class BlogPage(AbstractBasePage):
    parent_page_types = ["ocyan_plugin_wagtail_blog.BlogPageList"]
    subpage_types = []
    template = "blog/blog_item.html"

    post_date = models.DateField(default=datetime.date.today)
    intro = RichTextField()
    lead_image = models.ForeignKey(
        get_image_model_string(), on_delete=models.PROTECT, related_name="+"
    )
    magento_id = models.IntegerField(verbose_name="Magento Id", null=True, blank=True)

    body = StreamField(
        [
            ("heading", WrappedCharBlock(classname="full title", tag="h1")),
            ("paragraph", blocks.RichTextBlock()),
            ("image", ImageChooserBlock(template="blog/blog_image.html")),
        ],
        use_json_field=True,
    )

    content_panels = AbstractBasePage.content_panels + [
        MultiFieldPanel(
            [FieldPanel("post_date")], heading=_("Publication date (defaults to today)")
        ),
        MultiFieldPanel(
            [
                FieldPanel("lead_image", widget=AdminImageChooser),
                FieldPanel("intro"),
                FieldPanel("body"),
            ],
            heading="Content",
        ),
    ]

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

    def get_template_style(self):
        blog_index_page = self.get_blog_index_page()
        if hasattr(blog_index_page, "get_template_style"):
            return blog_index_page.get_template_style()
        return BLOG_TEMPLATE_STYLE_DEFAULT

    def get_detail_template_name(self):
        blog_index_page = self.get_blog_index_page()
        if hasattr(blog_index_page, "get_detail_template_name"):
            return blog_index_page.get_detail_template_name()
        return self.template

    def get_stylesheet_paths(self):
        blog_index_page = self.get_blog_index_page()
        if hasattr(blog_index_page, "get_stylesheet_paths"):
            return blog_index_page.get_stylesheet_paths()
        return ["wagtail_blog/css/blog.css"]

    def get_meta_description(self):
        if self.search_description:
            return self.search_description

        if self.intro:
            intro_text = strip_tags(self.intro).strip()
            if intro_text:
                return intro_text

        return None

    def get_meta_image_url(self, request):
        meta_image = self.get_meta_image_rendition()
        if meta_image:
            site_root = getattr(self.get_site(), "root_url", None)
            if site_root:
                return f"{site_root}{meta_image.url}"
            return meta_image.url
        return None

    def get_structured_data(self):
        structured_data = {
            "@context": "https://schema.org",
            "@type": "BlogPosting",
            "headline": self.get_meta_title(),
            "url": self.get_meta_url(),
            "mainEntityOfPage": self.get_meta_url(),
        }

        description = self.get_meta_description()
        if description:
            structured_data["description"] = description

        if self.post_date:
            structured_data["datePublished"] = self.post_date.isoformat()

        modified_at = getattr(self, "last_published_at", None) or getattr(
            self, "latest_revision_created_at", None
        )
        if modified_at:
            structured_data["dateModified"] = modified_at.isoformat()

        meta_image_url = self.get_meta_image_url(None)
        if meta_image_url:
            structured_data["image"] = meta_image_url

        return structured_data

    def get_structured_data_json(self):
        return json.dumps(self.get_structured_data())

    def get_canonical_url(self, request=None):
        return self.get_meta_url()

    def get_blog_index_page(self):
        return self.get_parent().get_specific()

    def get_sibling_blog_queryset(self):
        blog_index_page = self.get_blog_index_page()
        if hasattr(blog_index_page, "get_blog_queryset"):
            return blog_index_page.get_blog_queryset()

        queryset = BlogPage.objects.child_of(self.get_parent()).live()
        locale_id = getattr(self, "locale_id", None)
        if locale_id:
            queryset = queryset.filter(locale_id=locale_id)
        return queryset.order_by("-post_date")

    def get_recent_blog_queryset(self):
        return self.get_sibling_blog_queryset().exclude(pk=self.pk)

    def get_recent_blogs(self):
        return recent_from_queryset(self.get_recent_blog_queryset(), limit=4)

    def get_template(self, request, *args, **kwargs):
        return self.get_detail_template_name()

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        context["blogs"] = self.get_sibling_blog_queryset()
        context["recent_blogs"] = self.get_recent_blogs()
        context["canonical_url"] = self.get_canonical_url(request)
        context["blog_template_style"] = self.get_template_style()
        context["blog_stylesheet_paths"] = self.get_stylesheet_paths()
        return context

    def get_meta_image(self):
        if self.search_image:
            return self.search_image

        return self.lead_image

    class Meta:
        verbose_name = _("Blog entry")
        verbose_name_plural = _("Blog entries")


class PageMakerBlogPage(BlogPage):
    template = "blog/pagemaker_blog_item.html"

    content = RoadRunnerField(null=True, blank=True, use_json_field=True)

    content_panels = AbstractBasePage.content_panels + [
        MultiFieldPanel(
            [FieldPanel("post_date")], heading=_("Publication date (defaults to today)")
        ),
        MultiFieldPanel(
            [
                FieldPanel("lead_image", widget=AdminImageChooser),
                FieldPanel("intro"),
            ],
            heading="Summary content",
        ),
        RoadRunnerPanel("content"),
    ]

    def get_template(self, request, *args, **kwargs):
        blog_index_page = self.get_blog_index_page()
        if hasattr(blog_index_page, "get_pagemaker_detail_template_name"):
            return blog_index_page.get_pagemaker_detail_template_name()
        return self.template
