import os

from django.db import models

from wagtail.models import Page
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from wagtail.search import index
from .pagination import build_pagination_context, get_page_size

def _get_env_int(name, default):
    value = os.environ.get(name)
    if value is None:
        return default
    try:
        return int(value)
    except ValueError:
        return default

NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts
BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 7) # Default to 7 articles in block layout
HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 4 articles in horizon layout
PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination
CATEGORY_HOT_SIZE = _get_env_int("CATEGORY_HOT_SIZE", 6) # Default to 4 articles in category hot layout
RELATED_ARTICLES_SIZE = _get_env_int("RELATED_ARTICLES_SIZE", 4) # Default to 4 related articles

class BreadcrumbMixin:
    def build_breadcrumbs(self):
        site = self.get_site()
        site_root = site.root_page if site else None
        if site_root:
            ancestors = self.get_ancestors().specific().filter(depth__gt=site_root.depth)
        else:
            ancestors = self.get_ancestors().specific()
        breadcrumbs = list(ancestors) + [self]
        return breadcrumbs, site_root


# Mixin for Category-related functionality
class CategoryMixin:
    # Build category blocks
    def build_category_blocks(self, request=None):
        blocks = []
        subcategories = self.get_children().type(CategoryPage).live()
        if subcategories.exists():
            # If there are subcategories, create blocks for each
            for category in subcategories:
                blocks.append(
                    {
                        "title": category.title,
                        "items": ArticlePage.objects.child_of(category)
                        .live()
                        .order_by("-date", "-id")[:BLOCK_SIZE],
                        "url": category.url,
                        "layout": "block",
                    }
                )
        else:
            # If no subcategories, paginate articles under this category
            paginator = Paginator(
                ArticlePage.objects.child_of(self)
                .live()
                .order_by("-date", "-id"),
                get_page_size(request, PAGE_SIZE),
            )
            page_number = request.GET.get("page") if request else None

            try:
                page_obj = paginator.page(page_number)
            except PageNotAnInteger:
                page_obj = paginator.page(1)
            except EmptyPage:
                page_obj = paginator.page(paginator.num_pages)

            blocks.append(
                {
                    "title": self.title,
                    "items": page_obj,
                    "url": self.url,
                    "pagination": build_pagination_context(request, page_obj, paginator),
                }
            )
        return blocks

    # Get latest articles
    def get_latest_articles(self, request=None):
        latest_page = LatestPage.objects.first()
        if not request:
            # No request means no pagination (e.g., homepage)
            return {
                "title": latest_page.title,
                "items": ArticlePage.objects.filter(not_news=False).live().order_by("-date", "-id")[
                    :NEWS_SIZE
                ],
                "url": latest_page.url,
            }
        else:
            # Paginated view
            paginator = Paginator(
                ArticlePage.objects.live().order_by("-date", "-id"),
                get_page_size(request, PAGE_SIZE),
            )
            page_number = request.GET.get("page")

            try:
                page_obj = paginator.page(page_number)
            except PageNotAnInteger:
                page_obj = paginator.page(1)
            except EmptyPage:
                page_obj = paginator.page(paginator.num_pages)
            return {
                "title": self.title,
                "items": page_obj,
                "url": self.url,
                "pagination": build_pagination_context(request, page_obj, paginator),
            }

    def get_trending_articles(self, request=None, exclude_ids=None):
        trending_page = TrendingPage.objects.first()
        articles_qs = ArticlePage.objects.filter(trending=True).live().order_by(
            "-date", "-id"
        )

        # Exclude specified article IDs
        if exclude_ids:
            articles_qs = articles_qs.exclude(id__in=exclude_ids)

        if not request:
            # No request means no pagination (e.g., homepage)
            return {
                "title": trending_page.title,
                "items": articles_qs[:HORIZON_SIZE],
                "url": trending_page.url,
            }
        else:
            # Paginated view
            paginator = Paginator(articles_qs, get_page_size(request, PAGE_SIZE))
            page_number = request.GET.get("page")

            try:
                page_obj = paginator.page(page_number)
            except PageNotAnInteger:
                page_obj = paginator.page(1)
            except EmptyPage:
                page_obj = paginator.page(paginator.num_pages)
            return {
                "title": self.title,
                "items": page_obj,
                "url": self.url,
                "pagination": build_pagination_context(request, page_obj, paginator),
            }


class HomePage(Page, CategoryMixin):
    def get_context(self, request):
        context = super().get_context(request)

        sections = {
            "top_section": [],
            "category_sections": [],
        }

        latest_section = self.get_latest_articles().copy()
        # latest_section["layout"] = "block"
        sections["top_section"].append(latest_section)

        # NOT NEED TRENDING SECTION CURRENTLY
        # ------------------------------------------------------------------
        # Exclude latest articles from trending section
        # latest_items = latest_section.get("items", [])
        # if hasattr(latest_items, "values_list"):
        #     latest_ids = list(latest_items.values_list("id", flat=True))
        # else:
        #     latest_ids = [item.id for item in latest_items]

        # trending_section = self.get_trending_articles(
        #     exclude_ids=latest_ids
        # ).copy()
        # trending_section["layout"] = "horizon"
        # sections["top_section"].append(trending_section)
        # ------------------------------------------------------------------

        # Build category sections
        categories = CategoryPage.objects.child_of(self).live().in_menu()
        for category in categories:
            sections["category_sections"].append(
                {
                    "title": category.title,
                    "url": category.url,
                    "items": ArticlePage.objects.descendant_of(category)
                    .live()
                    .order_by("-date", "-id")[:HORIZON_SIZE],
                    "layout": "horizon",
                }
            )

        context["sections"] = sections
        return context


class LatestPage(Page, CategoryMixin, BreadcrumbMixin):
    template = "home/category_page.html"

    def get_context(self, request):
        context = super().get_context(request)
        context["category_sections"] = [self.get_latest_articles(request)]
        # breadcrumbs, site_root = self.build_breadcrumbs()
        # context["breadcrumbs"] = breadcrumbs
        # context["breadcrumb_root"] = site_root
        return context


class TrendingPage(Page, CategoryMixin, BreadcrumbMixin):
    template = "home/category_page.html"

    def get_context(self, request):
        context = super().get_context(request)
        context["category_sections"] = [self.get_trending_articles(request)]
        # breadcrumbs, site_root = self.build_breadcrumbs()
        # context["breadcrumbs"] = breadcrumbs
        # context["breadcrumb_root"] = site_root
        return context


class CategoryPage(Page, CategoryMixin, BreadcrumbMixin):
    @property
    def has_subcategories(self):
        return self.get_children().type(CategoryPage).live().exists()

    def get_context(self, request):
        context = super().get_context(request)
        context["category_sections"] = self.build_category_blocks(request)
        context["category_trending"] = (
            ArticlePage.objects.descendant_of(self)
            .live()
            .filter(trending=True)
            .order_by("-date", "-id")[:CATEGORY_HOT_SIZE]
        )
        # breadcrumbs, site_root = self.build_breadcrumbs()
        # context["breadcrumbs"] = breadcrumbs
        # context["breadcrumb_root"] = site_root
        return context


from wagtail.admin.panels import FieldPanel
from wagtail import blocks
from wagtail.images.blocks import ImageChooserBlock
from wagtail.fields import StreamField
from .blocks import ValidatingEmbedBlock, H2HeadingBlock, HorizontalRuleBlock

# HashTag for Article
class ArticlePageTag(TaggedItemBase):
    content_object = ParentalKey(
        "home.ArticlePage",
        related_name="tagged_items",
        on_delete=models.CASCADE,
    )

class ArticlePage(Page, BreadcrumbMixin):
    cover_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="列表封面圖",
    )
    banner_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="文章內文橫幅圖片",
    )
    date = models.DateTimeField("Published date")
    intro = models.CharField(max_length=250, blank=True)
    body = StreamField(
        [
            ("heading", H2HeadingBlock(form_classname="full title")),
            ("paragraph", blocks.RichTextBlock(features=["bold", "italic", "link"])),
            ("image", ImageChooserBlock()),
            ("embed", ValidatingEmbedBlock()),
            ("hr", HorizontalRuleBlock()),
            ("html", blocks.RawHTMLBlock(help_text="僅限信任來源的 blockquote/iframe 原始碼")),
        ],
        use_json_field=True,
    )
    trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示")
    not_news = models.BooleanField("Not News", default=False, help_text="不列入最新消息區塊")
    tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True)

    search_fields = Page.search_fields + [
        index.SearchField("intro", partial_match=True),
        index.SearchField("body_search_text", partial_match=True),
        index.SearchField("tag_names_search_text", partial_match=True),
    ]

    content_panels = Page.content_panels + [
        FieldPanel("trending"),
        FieldPanel("not_news"),
        FieldPanel("cover_image"),
        FieldPanel("banner_image"),
        FieldPanel("date"),
        FieldPanel("intro"),
        FieldPanel("body"),
        FieldPanel("tags"),
    ]

    def get_context(self, request):
        context = super().get_context(request)

        if self.cover_image:
            cover = self.cover_image.get_rendition("original")
            context["og_image"] = {
                "url": request.build_absolute_uri(cover.url),
                "width": cover.width,
                "height": cover.height,
                "alt": self.title,
            }

        breadcrumbs, site_root = self.build_breadcrumbs()
        # context["breadcrumbs"] = breadcrumbs
        # context["breadcrumb_root"] = site_root
        category_crumbs = [
            crumb
            for crumb in breadcrumbs
            if crumb.id != self.id and (not site_root or crumb.id != site_root.id)
        ]
        context["article_category"] = category_crumbs[-1] if category_crumbs else None

        tag_ids = list(self.tags.values_list("id", flat=True))
        if tag_ids:
            related_articles = (
                ArticlePage.objects.live()
                .exclude(id=self.id)
                .filter(tags__id__in=tag_ids)
                .distinct()
                .order_by("-date", "-id")[:RELATED_ARTICLES_SIZE]
            )
        else:
            related_articles = ArticlePage.objects.none()

        context["related_articles"] = related_articles
        return context

    @property
    def body_search_text(self):
        if not self.body:
            return ""

        excluded_types = {"image", "embed", "hr", "html"}
        chunks = []

        for block in self.body:
            if block.block_type in excluded_types:
                continue
            # Each block decides how to expose searchable text
            block_content = block.block.get_searchable_content(block.value)
            if block_content:
                chunks.extend(block_content)

        return " ".join(text for text in chunks if isinstance(text, str))

    @property
    def tag_names_search_text(self):
        return " ".join(self.tags.values_list("name", flat=True))
