from functools import cached_property

from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.paginator import Paginator
from django.db import models
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.utils import timezone
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from wagtail.admin.edit_handlers import (
    FieldPanel,
    HelpPanel,
    MultiFieldPanel,
    ObjectList,
    TabbedInterface,
)
from wagtail.admin.panels import PageChooserPanel
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.core.blocks import CharBlock, RichTextBlock
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page
from wagtailmetadata.models import MetadataPageMixin

from elections2021.constants import REGION_CHOICES  # pozor, import ze sousedního modulu
from shared.forms import SubscribeForm
from shared.models import (  # MenuMixin,
    ArticleMixin,
    ExtendedMetadataHomePageMixin,
    ExtendedMetadataPageMixin,
    SubpageMixin,
)
from shared.utils import make_promote_panels, subscribe_to_newsletter
from tuning import admin_help
from twitter_utils.models import Tweet

from . import blocks
from .constants import MONTH_NAMES
from .forms import JekyllImportForm
from .menu import MenuMixin


class ARTICLE_TYPES(models.IntegerChoices):
    WORK_TIMELINE = 1, "Článek na timeline Piráti pracují"
    PRESS_RELEASE = 2, "Tisková zpráva"


class MainHomePage(
    MenuMixin, RoutablePageMixin, ExtendedMetadataHomePageMixin, MetadataPageMixin, Page
):
    # header

    contact_newcomers_link = models.URLField(
        "URL pro zájemce o členství",
        blank=True,
        null=True,
        default="https://nalodeni.pirati.cz",
    )
    contact_newcomers_text = models.TextField(
        "Text na tlačítku pro zájemce o členství",
        blank=True,
        null=True,
        default="Nalodit se",
    )

    donation_page_link = models.URLField(
        "URL pro příjem darů (tlačítko Dary)",
        blank=True,
        null=True,
        default="https://dary.pirati.cz",
    )
    donation_page_text = models.TextField(
        "Text na tlačítku pro příjem darů",
        blank=True,
        null=True,
        default="Darovat",
    )

    # content
    content = StreamField(
        [
            ("carousel", blocks.HomePageCarouselBlock()),
            ("news", blocks.NewsBlock()),
            ("people", blocks.PeopleOverviewBlock()),
            ("regions", blocks.RegionsBlock()),
            ("tweets", blocks.TweetsBlock()),
            ("boxes", blocks.BoxesBlock()),
        ],
        verbose_name="Hlavní obsah",
        blank=True,
    )

    # footer
    footer_other_links = StreamField(
        [
            ("other_links", blocks.OtherLinksBlock()),
        ],
        verbose_name="Bloky dalších odkazů v zápatí webu",
        blank=True,
    )

    footer_person_list = StreamField(
        [("person", blocks.PersonContactBlock())],
        verbose_name="Osoby v zápatí webu",
        blank=True,
        max_num=6,
    )

    # settings
    gdpr_and_cookies_page = models.ForeignKey(
        "main.MainSimplePage",
        verbose_name="Stránka pro GDPR",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
    )

    matomo_id = models.IntegerField(
        "Matomo ID pro sledování návštěvnosti", blank=True, null=True
    )

    social_links = StreamField(
        [
            ("social_links", blocks.SocialLinkBlock()),
        ],
        verbose_name="Odkazy na sociální sítě v zápatí webu",
        blank=True,
    )

    twitter_usernames = StreamField(
        [("username", CharBlock(label="Twitter uživatelské jméno"))],
        verbose_name="Uživatelská jména pro synchronizované twitter účty",
        blank=True,
        max_num=64,
    )

    content_panels = Page.content_panels + [
        FieldPanel("content"),
        FieldPanel("footer_other_links"),
        FieldPanel("footer_person_list"),
    ]

    promote_panels = make_promote_panels(admin_help.build(admin_help.IMPORTANT_TITLE))

    settings_panels = [
        FieldPanel("contact_newcomers_link"),
        FieldPanel("contact_newcomers_text"),
        PageChooserPanel("gdpr_and_cookies_page"),
        FieldPanel("donation_page_link"),
        FieldPanel("donation_page_text"),
        FieldPanel("social_links"),
        FieldPanel("matomo_id"),
        FieldPanel("twitter_usernames"),
    ]

    ### EDIT HANDLERS

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading="Obsah"),
            ObjectList(promote_panels, heading="Propagovat"),
            ObjectList(settings_panels, heading="Nastavení"),
            ObjectList(MenuMixin.menu_panels, heading="Menu"),
        ]
    )

    ### RELATIONS

    subpage_types = [
        "main.MainArticlesPage",
        "main.MainProgramPage",
        "main.MainPeoplePage",
        "main.MainPersonPage",
        "main.MainSimplePage",
        "main.MainContactPage",
        "main.MainCrossroadPage",
    ]

    ### OTHERS

    class Meta:
        verbose_name = "HomePage pirati.cz"

    @cached_property
    def gdpr_and_cookies_url(self):
        if self.gdpr_and_cookies_page:
            return self.gdpr_and_cookies_page.url
        return "#"

    @staticmethod
    def get_404_response(request):
        return render(request, "main/404.html", status=404)

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

        twitter_username_list = [
            username_data["value"] for username_data in self.twitter_usernames.raw_data
        ]
        tweet_list = Tweet.objects.username_list(twitter_username_list).order_by(
            "-twitter_id"
        )

        context["tweet_list"] = tweet_list[:4]
        context["show_next_tweet"] = len(tweet_list) > 4

        context["regions"] = REGION_CHOICES

        context["article_data_list"] = MainArticlePage.objects.filter(
            region__isnull=False
        ).order_by("-date")[:3]

        articles_for_article_section = MainArticlePage.objects.filter(
            article_type=ARTICLE_TYPES.PRESS_RELEASE
        ).order_by("-date")
        context["article_main"] = (
            articles_for_article_section[0] if articles_for_article_section else None
        )
        context["article_carousel_list"] = articles_for_article_section[1:8]

        return context

    def get_region_response(self, request):
        if request.GET.get("region", None) == "VSK":
            sorted_article_qs = MainArticlePage.objects.filter(
                region__isnull=False
            ).order_by("-date")
        else:
            sorted_article_qs = MainArticlePage.objects.filter(
                region=request.GET.get("region", None)
            ).order_by("-date")
        context = {"article_data_list": sorted_article_qs[:3]}
        data = {
            "html": render(
                request, "main/includes/small_article_preview.html", context
            ).content.decode("utf-8")
        }
        return JsonResponse(data=data, safe=False)

    def get_twitter_response(self, request):
        twitter_username_list = [
            username_data["value"] for username_data in self.twitter_usernames.raw_data
        ]
        tweet_qs = Tweet.objects.username_list(twitter_username_list).order_by(
            "-twitter_id"
        )
        tweet_paginator = Paginator(tweet_qs, 4)

        tweet_page = tweet_paginator.get_page(request.GET.get("page", 1))
        context = {"tweet_list": tweet_page.object_list}
        html_content = render(
            request, "main/includes/twitter_widget.html", context
        ).content
        data = {
            "html": html_content.decode("utf-8"),
            "has_next": tweet_page.has_next(),
        }
        return JsonResponse(data=data, safe=False)

    def serve(self, request, *args, **kwargs):
        if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
            if "region" in request.GET:
                return self.get_region_response(request)
            else:
                return self.get_twitter_response(request)

        return super().serve(request, *args, **kwargs)

    @cached_property
    def newsletter_subscribe_url(self):
        return self.url + self.reverse_subpage("newsletter_subscribe")

    @property
    def articles_page(self):
        return self._first_subpage_of_type(MainArticlesPage)

    @property
    def root_page(self):
        return self

    @route(r"^prihlaseni-k-newsletteru/$")
    def newsletter_subscribe(self, request):
        if request.method == "POST":
            form = SubscribeForm(request.POST)
            if form.is_valid():
                subscribe_to_newsletter(
                    form.cleaned_data["email"],
                    settings.PIRATICZ_NEWSLETTER_ID,
                    settings.PIRATICZ_NEWSLETTER_SOURCE,
                )
                try:
                    page = (
                        Page.objects.filter(id=form.cleaned_data["return_page_id"])
                        .live()
                        .first()
                    )
                    return HttpResponseRedirect(page.full_url)
                except Page.DoesNotExist:
                    return HttpResponseRedirect(self.url)
        return HttpResponseRedirect(self.url)

    def _first_subpage_of_type(self, page_type) -> Page or None:
        try:
            return self.get_descendants().type(page_type).live().specific()[0]
        except IndexError:
            return None


class MainArticlesPage(
    ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
):
    perex = models.TextField()
    last_import_log = models.TextField(
        "Výstup z posledního importu", null=True, blank=True
    )

    import_panels = [
        MultiFieldPanel(
            [
                FieldPanel("do_import"),
                FieldPanel("collection"),
                FieldPanel("dry_run"),
                FieldPanel("jekyll_repo_url"),
                FieldPanel("readonly_log"),
                HelpPanel(
                    "Import provádějte vždy až po vytvoření stránky aktualit. "
                    'Pro uložení logu je nutné volit možnost "Publikovat", nikoliv'
                    'pouze "Uložit koncept". '
                    "Import proběhne na pozadí a může trvat až několik minut. "
                    "Dejte si po spuštění importu kávu a potom obnovte stránku pro "
                    "zobrazení výsledku importu."
                ),
            ],
            "import z Jekyll repozitáře",
        ),
    ]

    ### RELATIONS

    parent_page_types = ["main.MainHomePage"]
    subpage_types = ["main.MainArticlePage"]

    ### PANELS
    content_panels = Page.content_panels + [FieldPanel("perex")]
    promote_panels = make_promote_panels()

    ### EDIT HANDLERS

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading="Obsah"),
            ObjectList(promote_panels, heading="Propagovat"),
            ObjectList(import_panels, heading="Import"),
        ]
    )

    ### OTHERS

    base_form_class = JekyllImportForm

    class Meta:
        verbose_name = "Rozcestník článků"

    def get_article_data_list(self, months_back: int = 1):
        target_date_list = (
            MainArticlePage.objects.filter(article_type=ARTICLE_TYPES.WORK_TIMELINE)
            .order_by("-date")
            .values_list("date", flat=True)
        )

        if not target_date_list:
            return [self.get_empty_month_data(timezone.now().date())]

        target_date = target_date_list[0] - relativedelta(months=months_back)
        first_day_of_target_month = target_date.replace(day=1)

        sorted_article_qs = MainArticlePage.objects.filter(
            date__gt=first_day_of_target_month, article_type=ARTICLE_TYPES.WORK_TIMELINE
        ).order_by("-date")

        article_data_list = []

        current_month_data = self.get_empty_month_data(timezone.now().date())

        article_counter = 1
        for article in sorted_article_qs:
            if article.date.month != current_month_data["month_number"]:
                article_data_list.append(current_month_data)  # append completed month
                current_month_data = self.get_empty_month_data(article.date)
                article_counter = 1

            current_column = "left_column" if article_counter % 2 else "right_column"
            current_month_data[current_column].append(article)
            article_counter += 1

        article_data_list.append(current_month_data)  # last iteration

        return article_data_list

    def get_context(self, request, *args, **kwargs):
        ctx = super().get_context(request, args, kwargs)

        article_timeline_list = self.get_article_data_list(1)
        ctx["article_timeline_list"] = article_timeline_list
        ctx["show_next_timeline_articles"] = MainArticlePage.objects.filter(
            article_type=ARTICLE_TYPES.WORK_TIMELINE
        ).count() > len(article_timeline_list)

        article_list = MainArticlePage.objects.filter(
            article_type=ARTICLE_TYPES.PRESS_RELEASE
        ).order_by("-date")[
            :11
        ]  # dám LIMIT +1, abych věděl, jestli má cenu show_next
        ctx["article_article_list"] = article_list[:10]
        ctx["show_next_article"] = len(article_list) > 10

        return ctx

    def get_timeline_articles_response(self, request):
        article_list = self.get_article_data_list(int(request.GET.get("months", None)))
        context = {"article_data_list": article_list}
        data = {
            "html": render(
                request, "main/blocks/articles_timeline_block.html", context
            ).content.decode("utf-8"),
            "last_article": article_list[-1]
            == MainArticlePage.objects.filter(article_type=ARTICLE_TYPES.WORK_TIMELINE)
            .order_by("-date")
            .last(),
        }
        return JsonResponse(data=data, safe=False)

    def get_articles_response(self, request):
        article_paginator = Paginator(
            MainArticlePage.objects.filter(
                article_type=ARTICLE_TYPES.PRESS_RELEASE
            ).order_by("-date", "title"),
            10,
        )
        article_page = article_paginator.get_page(request.GET.get("page", 1))
        context = {"article_data_list": article_page.object_list}
        html_content = render(
            request, "main/includes/person_article_preview.html", context
        ).content
        data = {
            "html": html_content.decode("utf-8"),
            "has_next": article_page.has_next(),
        }
        return JsonResponse(data=data, safe=False)

    def serve(self, request, *args, **kwargs):
        if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
            if "months" in request.GET:
                return self.get_timeline_articles_response(request)
            if "page" in request.GET:
                return self.get_articles_response(request)

        return super().serve(request, *args, **kwargs)

    @staticmethod
    def get_empty_month_data(date_obj):
        return {
            "month_number": date_obj.month,
            "month_text": MONTH_NAMES[date_obj.month - 1],
            "left_column": [],
            "right_column": [],
        }


class MainArticleTag(TaggedItemBase):
    content_object = ParentalKey("main.MainArticlePage", on_delete=models.CASCADE)


class MainArticlePage(
    ArticleMixin, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
):
    ### FIELDS
    article_type = models.PositiveSmallIntegerField(
        "Typ článku", choices=ARTICLE_TYPES.choices, default=ARTICLE_TYPES.PRESS_RELEASE
    )
    is_black = models.BooleanField("Má tmavé pozadí?", default=False)

    content = StreamField(
        [
            ("text", RichTextBlock(template="main/blocks/rich_text_block.html")),
            ("quote", blocks.ArticleQuoteBlock()),
            ("download", blocks.ArticleDownloadBlock()),
            ("image", blocks.ArticleImageBlock()),
        ],
        verbose_name="Článek",
        blank=True,
    )

    author_page = models.ForeignKey(
        "main.MainPersonPage",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Stránka autora (osoby)",
    )
    region = models.CharField(
        choices=REGION_CHOICES,
        null=True,
        blank=True,
        max_length=3,
        verbose_name="Kraj",
        help_text="Kraj, ke kterému se článek vztahuje",
    )
    tags = ClusterTaggableManager(through=MainArticleTag, blank=True)

    ### PANELS

    content_panels = ArticleMixin.content_panels + [
        FieldPanel("article_type"),
        FieldPanel("author_page"),
        FieldPanel("is_black"),
        FieldPanel("region"),
        FieldPanel("tags"),
    ]

    promote_panels = make_promote_panels(
        admin_help.build(admin_help.NO_SEO_TITLE, admin_help.NO_DESCRIPTION_USE_PEREX),
        search_image=False,
    )

    ### RELATIONS

    parent_page_types = ["main.MainArticlesPage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Aktualita"

    # def get_context(self, request): chceme/nechceme?
    #     context = super().get_context(request)
    #     context["related_articles"] = (
    #         self.get_siblings(inclusive=False)
    #         .live()
    #         .specific()
    #         .order_by("-mainarticlepage__date")[:3]
    #     )
    #     return context


class MainProgramPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page):
    ### FIELDS

    perex = RichTextField()
    program = StreamField(
        [("program_group", blocks.ProgramGroupBlock(label="Část programu"))],
        verbose_name="Program",
        blank=True,
    )

    ### PANELS

    content_panels = Page.content_panels + [FieldPanel("perex"), FieldPanel("program")]

    promote_panels = make_promote_panels()

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["main.MainHomePage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Program"


class MainPeoplePage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page):
    ### FIELDS

    perex = RichTextField()
    people = StreamField(
        [("people_group", blocks.PeopleGroupBlock(label="Seznam osob"))],
        verbose_name="Lidé",
        blank=True,
    )

    ### PANELS

    content_panels = Page.content_panels + [FieldPanel("perex"), FieldPanel("people")]

    promote_panels = make_promote_panels()

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["main.MainHomePage"]
    subpage_types = ["main.MainPersonPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Lidé"


class MainPersonPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page):
    ### FIELDS
    main_image = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name="Hlavní obrázek",
        related_name="+",
    )

    profile_image = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name="Profilový obrázek",
        related_name="+",
    )
    before_name = models.CharField(
        "Tituly před jménem", max_length=32, blank=True, null=True
    )
    after_name = models.CharField(
        "Tituly za jménem", max_length=16, blank=True, null=True
    )
    position = models.CharField(
        "Pozice/povolání", max_length=200, blank=True, null=True
    )
    perex = models.TextField()
    text = RichTextField()

    twitter_username = models.CharField(
        "Uživatelské jméno twitter pro získání příspěvků",
        blank=True,
        null=True,
        max_length=32,
        help_text="Uživatelské jméno zadejte bez @ na začátku",
    )

    social_links = StreamField(
        [
            ("social_links", blocks.SocialLinkBlock()),
        ],
        verbose_name="Odkazy na sociální sítě",
        blank=True,
    )

    people = StreamField(
        [("people_group", blocks.PeopleGroupBlock(label="Seznam osob"))],
        verbose_name="Další lidé",
        blank=True,
    )

    email = models.CharField("E-mail", max_length=128, blank=True, null=True)
    phone = models.CharField("Telefonní kontakt", max_length=16, blank=True, null=True)

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["main.MainPeoplePage"]
    subpage_types = []

    ### PANELS
    content_panels = Page.content_panels + [
        FieldPanel("main_image"),
        FieldPanel("profile_image"),
        FieldPanel("before_name"),
        FieldPanel("after_name"),
        FieldPanel("position"),
        FieldPanel("perex"),
        FieldPanel("twitter_username"),
        FieldPanel("text"),
        FieldPanel("email"),
        FieldPanel("phone"),
        FieldPanel("social_links"),
        FieldPanel("people"),
    ]

    def get_context(self, request):
        context = super().get_context(request)
        context["article_page_list"] = MainArticlePage.objects.filter(
            author_page=self.id
        )
        context["tweet_list"] = Tweet.objects.username(self.twitter_username).order_by(
            "-twitter_id"
        )[:20]
        return context

    ### OTHERS

    class Meta:
        verbose_name = "Detail osoby"
        # ordering = ("title",)

    def get_background_photo(self):
        """
        Vrací background_photo pro pozadí na stránce, pokud není nastaveno,
        vezme falbback z homepage
        """
        return (
            self.background_photo
            if self.background_photo
            else self.root_page.fallback_image
        )


class MainSimplePage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page):
    ### FIELDS

    # content
    content = StreamField(
        [
            ("text", RichTextBlock(template="main/blocks/rich_text_block.html")),
        ],
        verbose_name="Hlavní obsah",
        blank=True,
    )

    ### PANELS

    content_panels = Page.content_panels + [FieldPanel("content")]

    promote_panels = make_promote_panels()

    settings_panels = []

    ### RELATIONS

    parent_page_types = [
        "main.MainHomePage",
        "main.MainSimplePage",
        "main.MainCrossroadPage",
    ]
    subpage_types = ["main.MainSimplePage"]

    ### OTHERS
    class Meta:
        verbose_name = "Jednoduchá stárnka"


class MainContactPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page):
    ### FIELDS

    contact_people = StreamField(
        [("item", blocks.PersonContactBlock())],
        verbose_name="Kontaktní osoby",
        blank=True,
    )
    contact_boxes = StreamField(
        [("item", blocks.PersonContactBoxBlock())],
        verbose_name="Kontaktní boxy",
        blank=True,
    )
    text = StreamField(
        [("two_columns_text", blocks.TwoTextColumnBlock())],
        verbose_name="Kontaktní informace",
        blank=True,
    )

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("text"),
        FieldPanel("contact_people"),
        FieldPanel("contact_boxes"),
    ]

    promote_panels = make_promote_panels()

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["main.MainHomePage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Kontakty"


class MainCrossroadPage(
    ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
):

    ### FIELDS

    headlined_cards_content = StreamField(
        [(("headlined_cards"), blocks.CardLinkWithHeadlineBlock())],
        verbose_name="Karty rozcestníku s nadpisem",
        blank=True,
    )
    cards_content = StreamField(
        [("cards", blocks.CardLinkBlock())],
        verbose_name="Karty rozcestníku",
        blank=True,
    )

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("headlined_cards_content"),
        FieldPanel("cards_content"),
    ]

    promote_panels = make_promote_panels()

    settings_panels = []

    ### RELATIONS

    parent_page_types = [
        "main.MainHomePage",
        "main.MainCrossroadPage",
    ]
    subpage_types = [
        "main.MainSimplePage",
        "main.MainCrossroadPage",
    ]
    ### OTHERS

    class Meta:
        verbose_name = "Rozcestník s kartami"