import logging
from datetime import date, timedelta
from pathlib import Path

import arrow
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from icalevnt import icalevents
from wagtail.admin.panels import FieldPanel
from wagtail.models import Page
from wagtailmetadata.models import MetadataPageMixin

from shared.models import SubpageMixin

from .parser import process_event_list

logger = logging.getLogger(__name__)


def _convert_arrow_to_datetime(event):
    event["start"] = event["start"].datetime
    event["end"] = event["end"].datetime
    return event


class EventsJSONField(models.JSONField):
    """
    JSONField for lists of events which converts `begin` and `end` to datetime
    on load from DB.
    """

    def from_db_value(self, value, expression, connection):
        value = super().from_db_value(value, expression, connection)
        if value:
            for event in value:
                event["start"] = arrow.get(event.get("start")).datetime
                event["end"] = arrow.get(event["end"]).datetime
        return value


class Calendar(models.Model):
    CURRENT_NUM = 6

    url = models.URLField()
    event_hash = models.CharField(max_length=256, null=True)
    last_update = models.DateTimeField(null=True)
    past_events = EventsJSONField(encoder=DjangoJSONEncoder, null=True)
    future_events = EventsJSONField(encoder=DjangoJSONEncoder, null=True)

    def current_events(self):
        return self.future_events[: self.CURRENT_NUM]

    def handle_event_list(self, event_list):
        event_list_hash = str(hash(str(event_list)))
        if event_list_hash != self.event_hash:
            past, future = process_event_list(event_list)
            self.past_events = past
            self.future_events = future
            self.event_hash = event_list_hash

        self.last_update = arrow.utcnow().datetime
        self.save()

    def update_source(self):
        event_list = icalevents.events(
            url=self.url,
            start=date.today() - timedelta(days=30),
            end=date.today() + timedelta(days=60),
        )
        self.handle_event_list(event_list)


class CalendarMixin(models.Model):
    """
    Mixin to be used in other models, like site settings, which adds relation
    to Calendar.
    """

    calendar_url = models.URLField(
        "URL kalendáře ve formátu iCal", blank=True, null=True
    )
    calendar = models.ForeignKey(
        Calendar, null=True, blank=True, on_delete=models.PROTECT
    )
    calendar_page = models.ForeignKey(
        "calendar_utils.CalendarPage",
        verbose_name="Stránka s kalendářem",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        # create or update related Calendar
        if self.calendar_url:
            if self.calendar:
                if self.calendar.url != self.calendar_url:
                    self.calendar.url = self.calendar_url
                    self.calendar.save()
            else:
                self.calendar = Calendar.objects.create(url=self.calendar_url)

            try:
                self.calendar.update_source()
            except:
                logger.error(
                    "Calendar update failed for %s", self.calendar.url, exc_info=True
                )

        # delete related Calendar when URL is cleared
        if not self.calendar_url and self.calendar:
            self.calendar = None

        super().save(*args, **kwargs)


class CalendarPage(SubpageMixin, MetadataPageMixin, CalendarMixin, Page):
    """
    Page for displaying full calendar
    """

    calendar_url = models.URLField(
        "URL kalendáře ve formátu iCal", blank=False, null=True
    )

    ### PANELS

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

    ### RELATIONS

    parent_page_types = [
        "district.DistrictCenterPage",
        "district.DistrictHomePage",
        "elections2021.Elections2021CalendarPage",
        "senat_campaign.SenatCampaignHomePage",
        "uniweb.UniwebHomePage",
    ]
    subpage_types = []

    ### OTHERS

    def get_template(self, request):
        """
        Allows this template to dynamically load correct calendar_page, based on root page which helps it determine from
        which project the page should be loaded
        """
        module = self.root_page.__class__.__module__  # Example: "district.module"
        pathname = module.split(".")[0]  # Gets "district" from "district.module"
        root = str(Path(__file__).parents[2])
        project = root + "/majak"
        return (
            project + "/" + pathname + "/templates/" + pathname + "_calendar_page.html"
        )

    class Meta:
        verbose_name = "Stránka s kalendářem"