import logging
import re
from datetime import timedelta

from taggit.managers import TaggableManager

from django.contrib.auth.models import User
from django.core.validators import ValidationError
from django.db import models
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _

from core.models import CustomFlatPage
from documents import create_pad, get_pad_html, set_pad_html
from .managers import (
    PostManager,
    ArticleManager,
    NewsManager,
    AllEventManager,
    EventManager,
    FetMeetingManager,
)

logger = logging.getLogger(__name__)
request_logger = logging.getLogger("django.request")


def create_pad_for_post(slug, item="agenda"):
    """
    Create a Etherpad pad.

    Return a Etherpad key.
    """
    logger.info(f"Pad-Type: {item}")
    padID = create_pad(slug + "-" + item)
    if padID:
        # set template into the newly created pad if it exists
        page = CustomFlatPage.objects.filter(title__iexact=item).first()
        if page:
            set_pad_html(padID, page.content)
            logger.info(f"Template gesetzt von: {page.title}")
    else:
        padID = f"{slug}-{item}"

    logger.info(f"PadID: {padID}")
    return padID


class Category(models.Model):
    # Titel des Posts
    title = models.CharField(max_length=200)

    subtitle = models.CharField(max_length=500, null=True, blank=True)
    # Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
    slug = models.SlugField(unique=True, null=True, blank=True)
    # Ein Haupt Bild für den Post
    image = models.ImageField(null=True, blank=True)

    tags = TaggableManager(blank=True)

    class Meta:
        verbose_name = "Category"
        verbose_name_plural = "Categories"


class Post(models.Model):
    # legacy id is for the posts from the old website
    legacy_id = models.IntegerField(null=True, blank=True)

    title = models.CharField(verbose_name="Titel", max_length=200)
    subtitle = models.CharField(max_length=500, null=True, blank=True)
    tags = TaggableManager(blank=True)

    # Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
    slug = models.SlugField(unique=True, blank=True)

    body = models.TextField(null=True, blank=True)
    image = models.ImageField(null=True, blank=True)

    author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    public_date = models.DateField(
        verbose_name="Veröffentlichung", null=True, blank=True, default=timezone.now
    )

    __choices = [("N", _("News")), ("E", _("Event")), ("F", _("FetMeeting"))]
    post_type = models.CharField(max_length=1, choices=__choices, editable=True)

    class Status(models.TextChoices):
        DRAFT = "10", _("DRAFT")
        ONLY_INTERN = "15", _("ONLY_INTERN")
        PUBLIC = "20", _("PUBLIC")

    status = models.CharField(
        verbose_name="Status",
        max_length=2,
        choices=Status.choices,
        default=Status.DRAFT,
    )

    # post is pinned at main page
    is_pinned = models.BooleanField(verbose_name="ANGEHEFTET", default=False)

    # addional infos for events
    event_start = models.DateTimeField(
        verbose_name="Event Start", null=True, blank=True
    )
    event_end = models.DateTimeField(verbose_name="Event Ende", null=True, blank=True)
    event_place = models.CharField(max_length=200, null=True, blank=True)

    # protocol for fet meeting
    has_protocol = models.BooleanField(default=False)
    has_agenda = models.BooleanField(default=False)
    protocol_key = models.CharField(max_length=200, null=True, blank=True)
    agenda_key = models.CharField(max_length=200, null=True, blank=True)

    # TimeStamps
    date_modified = models.DateTimeField(auto_now=True)
    date_created = models.DateTimeField(auto_now_add=True)

    # useless (-_-)
    legacy_rubrik_id = models.IntegerField(null=True, blank=True)
    imported_from = models.CharField(max_length=200, null=True, blank=True)

    # Managers
    objects = PostManager()
    articles = ArticleManager()

    def __str__(self):
        return "Post (%s, %s): %s" % (
            self.slug,
            self.public_date.strftime("%d.%m.%Y"),
            self.title,
        )

    @property
    def url(self):
        # TODO: replace with get_absolute_url
        return reverse("posts:posts.show", kwargs={"id": self.slug})

    def get_absolute_url(self):
        return self.url

    def save(self, *args, **kwargs):
        # save the post with some defaults
        if not self.public_date:
            self.public_date = timezone.now().date()

        if not self.slug:
            self.slug = slugify(self.public_date) + "-" + slugify(self.title)

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

        self.tags.set(
            [
                *re.findall(r"\#([\d\w-]+)", str(self.subtitle)),
                *re.findall(r"\#([\d\w-]+)", str(self.title)),
            ]
        )

    @property
    def agenda_html(self):
        "Agenda HTML from Etherpad Pad"
        if not self.agenda_key:
            return None

        return get_pad_html(self.agenda_key)

    @property
    def protocol_html(self):
        "Protocol HTML from Etherpad Pad"
        if not self.protocol_key:
            return None

        return get_pad_html(self.protocol_key)

    @agenda_html.setter
    def agenda_html(self, value):
        if not self.agenda_key:
            self.agenda_key = self.get_agenda_key()
        if not value or not self.agenda_key:
            return None

        set_pad_html(self.agenda_key, value)

        request_logger.info(f"set etherpad for post {self.slug} id: {self.agenda_key}")
        return value

    @protocol_html.setter
    def protocol_html(self, value):
        if not self.protocol_key:
            self.protocol_key = self.get_protocol_key()
        if not value or not self.protocol_key:
            return None

        set_pad_html(self.protocol_key, value)

        request_logger.info(
            f"set etherpad for post {self.slug} id: {self.protocol_key}"
        )
        return value

    def get_agenda_key(self):
        """
        Create a Etherpad Id for the Pad associated to this post.
        Create the pad if it doesn't exist.
        """
        if not self.slug:
            return None
        return create_pad_for_post(self.slug, "agenda")

    def get_protocol_key(self):
        """
        Create a Etherpad Id for the Pad associated to this post.
        Create the pad if it doesn't exist.
        """
        if not self.slug:
            return None
        return create_pad_for_post(self.slug, "protocol")

    def get_tags(self):
        """
        Returns assigned tags as a comma seperated list.
        """
        return ",".join(self.tags.names())

    @property
    def get_tagnames(self):
        return ["#%s" % t for t in self.tags.names()]

    @property
    def tag_string(self):
        return self.get_tags()

    @property
    def imageurl(self):
        """
        returns the url to the image
        """
        if self.image:
            return self.image.url

        image = self.find_an_image()
        if image:
            return image.url
        return ""

    def find_an_image(self):
        """
        find an image via another post
        """
        # TODO: Explain why this image is selected on save of the image
        # Query all posts that have a slug that equals one of the tags
        posts1 = (
            Post.objects.filter(slug__in=self.tags.names())
            .filter(image__isnull=False)[0:1]
            .all()
        )
        if len(posts1) > 0:
            return posts1.get().image

        return None

    def clean(self):
        if self.event_end and self.event_end < self.event_start:
            raise ValidationError(_("Das Ende des Events liegt vor dem Beginn."))
        if self.event_start and self.post_type not in ["E", "F"]:
            raise ValidationError(
                _("Für diesen Post Typ ist kein Event Start zulässig")
            )
        super().clean()

    @property
    def published(self):
        if self.status == self.Status.PUBLIC:
            return True

        return False


class News(Post):
    objects = NewsManager()

    class Meta:
        proxy = True

        verbose_name = "News"
        verbose_name_plural = "News"

    def save(self, *args, **kwargs):
        if not self.post_type:
            self.post_type = "N"

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


class Event(Post):
    only_events = EventManager()
    all_events = AllEventManager()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.post_type = "E"

    class Meta:
        proxy = True

        verbose_name = "Event"
        verbose_name_plural = "Events"

    def save(self, *args, **kwargs):
        if not self.post_type:
            self.post_type = "E"
        if not self.event_end:
            self.event_end = self.event_start + timedelta(hours=2)

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

    def clean(self):
        if not self.event_start:
            raise ValidationError(_("Das Datum des Events fehlt."))
        super().clean()


class FetMeeting(Event):
    objects = FetMeetingManager()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.post_type = "F"

    class Meta:
        proxy = True

        verbose_name = "Fet Sitzung"
        verbose_name_plural = "Fet Sitzungen"

    def save(self, *args, **kwargs):
        self.title = "Fachschaftssitzung"
        if not self.slug:
            self.slug = self.__get_slug()

        self.has_agenda = True
        self.has_protocol = True

        self.agenda_key = self.get_agenda_key()
        self.protocol_key = self.get_protocol_key()

        if not self.post_type:
            self.post_type = "F"

        if not self.event_place:
            self.event_place = "FET"

        # make duration 2 hours if not specified otherwise
        if not self.event_end:
            self.event_end = self.event_start + timedelta(hours=2)

        # set FET Meeting always public
        self.status = self.Status.PUBLIC

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

    def __get_slug(self):
        slug = slugify(self.event_start.date()) + "-" + slugify("Fachschaftssitzung")

        if Post.objects.filter(slug=slug).exists():
            if Post.objects.get(slug=slug).id != self.id:
                raise ValidationError(
                    _("Es existiert bereits eine Sitzung mit demselben Datum.")
                )

        return slug

    def clean(self):
        super().clean()
        if not self.slug:
            self.slug = self.__get_slug()


class FileUpload(models.Model):
    title = models.CharField(verbose_name="Titel", max_length=200)
    file_field = models.FileField(
        verbose_name="Dokument", upload_to="uploads/posts/files/"
    )
    post = models.ForeignKey(Post, on_delete=models.CASCADE)

    objects = models.Manager()

    def __str__(self):
        return self.title
