import json
import logging

import requests
from datetime import date, timedelta
from django.core.cache import cache
from django.db import models
from django.utils import timezone
from icalevnt import icalevents
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, PublishingPanel
from wagtail.fields import StreamField
from wagtail.models import Page

from instagram_utils.models import InstagramPost
from shared.blocks import DEFAULT_CONTENT_BLOCKS, MenuItemBlock, MenuParentBlock

logger = logging.getLogger(__name__)


class SubpageMixin:
    """Must be used in class definition before MetadataPageMixin!"""

    @property
    def root_page(self):
        if not hasattr(self, "_root_page"):
            # vypada to hackove ale lze takto pouzit: dle dokumentace get_ancestors
            # vraci stranky v poradi od rootu, tedy domovska stranka je druha v poradi
            self._root_page = self.get_ancestors().specific()[1]
        return self._root_page

    def get_meta_image(self):
        return self.search_image or self.root_page.get_meta_image()


class ArticleMixin(models.Model):
    """
    Common fields for articles.

    Must be used in class definition before MetadataPageMixin!

    If you want to tag articles, add tags as `tags` field in article page model.
    """

    ### FIELDS

    content = StreamField(
        DEFAULT_CONTENT_BLOCKS,
        verbose_name="Článek",
        blank=True,
        use_json_field=True,
    )
    date = models.DateField("datum", default=timezone.now)
    perex = models.TextField("perex")
    author = models.CharField("autor", max_length=250, blank=True, null=True)
    image = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name="obrázek",
    )

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("date"),
        FieldPanel("perex"),
        FieldPanel("content"),
        FieldPanel("author"),
        FieldPanel("image"),
    ]

    settings_panels = [PublishingPanel()]

    class Meta:
        abstract = True

    @classmethod
    def has_tags(cls):
        try:
            cls._meta.get_field("tags")
        except models.FieldDoesNotExist:
            return False
        return True

    def tag_filter_page(self):
        """Page used for filtering by tags in url like `?tag=foo`."""
        return self.get_parent()

    def get_meta_image(self):
        if hasattr(self, "search_image") and self.search_image:
            return self.search_image
        return self.image

    def get_meta_description(self):
        if hasattr(self, "search_description") and self.search_description:
            return self.search_description
        return self.perex


class MenuMixin(Page):
    menu = StreamField(
        [("menu_item", MenuItemBlock()), ("menu_parent", MenuParentBlock())],
        verbose_name="Menu",
        blank=True,
        use_json_field=True,
    )

    menu_panels = [
        MultiFieldPanel(
            [
                FieldPanel("menu"),
            ],
            heading="Menu Options",
        ),
    ]

    class Meta:
        abstract = True


class ExtendedMetadataHomePageMixin(models.Model):
    """Use for site home page to define metadata title suffix.

    Must be used in class definition before MetadataPageMixin!
    """

    title_suffix = models.CharField(
        "Přípona titulku stránky",
        max_length=100,
        blank=True,
        null=True,
        help_text="Umožňuje přidat příponu k základnímu titulku stránky. Pokud "
        "je např. titulek stránky pojmenovaný 'Kontakt' a do přípony vyplníte "
        "'MS Pardubice | Piráti', výsledný titulek bude "
        "'Kontakt | MS Pardubice | Piráti'. Pokud příponu nevyplníte, použije "
        "se název webu.",
    )

    class Meta:
        abstract = True

    def get_meta_title_suffix(self):
        if self.title_suffix:
            return self.title_suffix

        if hasattr(super(), "get_meta_title"):
            return super().get_meta_title()

        return self.get_site().site_name

    def get_meta_title(self):
        title = super().get_meta_title()
        suffix = self.get_meta_title_suffix()

        # Covers scenario when title_suffix is not set and evaluates to super().get_meta_title() value.
        # Rather than having MS Pardubice | MS Pardubice, just use MS Pardubice alone.
        if title != suffix:
            return f"{super().get_meta_title()} | {self.get_meta_title_suffix()}"

        return title


class ExtendedMetadataPageMixin(models.Model):
    """Use for pages except for home page to use shared metadata title suffix.

    There are few rules on how to use this:

    - Do not forget to list ExtendedMetadataHomePageMixin among ancestors of the related HomePage class.
    - Must be used in class definition before MetadataPageMixin.
    - Expects SubpageMixin or equivalent exposing `root_page` property to be used for the page too.
    """

    class Meta:
        abstract = True

    def get_meta_title_suffix(self):
        if not hasattr(self, "root_page"):
            logger.warning(
                "Using `ExtendedMetadataPageMixin` without `SubpageMixin` for %s",
                repr(self),
            )
            return None

        if not hasattr(self.root_page, "get_meta_title_suffix"):
            logger.warning(
                "Using `ExtendedMetadataPageMixin` without `ExtendedMetadataHomePageMixin` on the root page for %s",
                repr(self),
            )
            return None

        return self.root_page.get_meta_title_suffix()

    def get_meta_title(self):
        suffix = self.get_meta_title_suffix()

        if not suffix:
            return super().get_meta_title()

        return f"{super().get_meta_title()} | {self.get_meta_title_suffix()}"


class PersonCalendarMixin(models.Model):
    ical_calendar_url = models.URLField(
        "iCal adresa kalendáře",
        max_length=256,
        blank=True,
        null=True,
        help_text=(
            "Podporuje Mrak, Google Kalendář a další. Návod na synchronizaci najdeš "
            "na pi2.cz/kalendare"
        ),
    )

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

        if self.ical_calendar_url:
            context["calendar_data"] = self.get_ical_data()

        return context

    def get_ical_data(self) -> list:
        ical_response = cache.get(f"calendar_{self.ical_calendar_url}")

        if ical_response is None:
            ical_response = requests.get(self.ical_calendar_url)
            ical_response.raise_for_status()
            ical_response = ical_response.text

            cache.set(
                f"calendar_{self.ical_calendar_url}",
                ical_response,
                timeout=3600,  # 1 hour
            )

        parsed_events = icalevents.parse_events(
            ical_response,
            start=date.today() - timedelta(days=30),
            end=date.today() + timedelta(days=60),
        )

        calendar_format_events = []

        for event in parsed_events:
            parsed_event = {
                "allDay": event.all_day,
                "start": event.start.isoformat(),
                "end": event.end.isoformat(),
            }

            if event.summary is not None:
                parsed_event["title"] = event.summary

            if event.url is not None:
                parsed_event["url"] = event.url

            calendar_format_events.append(parsed_event)

        return json.dumps(calendar_format_events)

    class Meta:
        abstract = True