"""Webapp app models."""

from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django.db.models.signals import post_save
from django.dispatch import receiver

# from datetime import timedelta
import requests
from uuid import uuid4
import logging
from django.utils.translation import gettext_lazy as _
from django.db.models import JSONField
from auditlog.registry import auditlog
from polymorphic.models import PolymorphicModel
from webapp.utils import month_from_now, send_signup_email
from sikweb.settings import FRONTEND_URL

VERBOSE_NAME = _("Webapp")
EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"


class Tag(models.Model):
    """Model for tag."""

    class Meta:
        verbose_name = _("Tag")
        verbose_name_plural = _("Tags")

    id = models.AutoField(primary_key=True)
    slug = models.SlugField(unique=True)
    name = models.CharField(max_length=127)
    icon = models.ImageField()

    def __str__(self):
        return _("Tag: {}").format(self.slug)


class BaseFeed(models.Model):
    """Model containing something showing on some info feed."""

    id = models.AutoField(primary_key=True)
    tags = models.ManyToManyField(Tag, related_name="feeds", blank=True)
    visible = models.BooleanField(default=True)
    title = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    content = models.TextField()
    image = models.ImageField(blank=True, null=True)


class Feed(BaseFeed):
    """Model representing feed."""

    class Meta:
        verbose_name = _("Feed")
        verbose_name_plural = _("Feeds")

    publish_time = models.DateTimeField(default=timezone.now)
    autohide = models.DateTimeField(default=month_from_now)
    autohide_enabled = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        delete_str = _("Deleted: ") if self.deleted else ""
        return _("{}Feed: {}").format(delete_str, self.title)

    __previousVisible = False

    def __init__(self, *args, **kwargs):
        super(Feed, self).__init__(*args, **kwargs)
        self.__previousVisible = self.visible

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        created = self.pk is None
        super(Feed, self).save(force_insert, force_update, *args, **kwargs)

        if self.visible and (created or not self.__previousVisible):
            self.refresh_from_db()  # Fetch so we can use primary key
            url = f"https://{FRONTEND_URL}/feed/{self.pk}"
            processHooks(
                message=generateMessage(
                    "Uusi uutinen", self.title, self.description, url
                ),
                eventType="feed",
            )
        self.__previousVisible = self.visible


class Event(BaseFeed):
    """Model for event in guild calendar"""

    class Meta:
        verbose_name = _("Event")
        verbose_name_plural = _("Events")

    start_time = models.DateTimeField(default=timezone.now)
    end_time = models.DateTimeField(default=timezone.now)
    signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event")
    location = models.CharField(max_length=255, blank=True)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        delete_str = _("Deleted: ") if self.deleted else ""
        return _("{}Event: {}").format(delete_str, self.title)

    __previousVisible = False

    def __init__(self, *args, **kwargs):
        super(Event, self).__init__(*args, **kwargs)
        self.__previousVisible = self.visible

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        created = self.pk is None
        super(Event, self).save(force_insert, force_update, *args, **kwargs)

        if self.visible and (created or not self.__previousVisible):
            self.refresh_from_db()  # Fetch so we can use primary key
            url = f"https://{FRONTEND_URL}/events/{self.pk}"
            processHooks(
                message=generateMessage(
                    "Uusi tapahtuma", self.title, self.description, url
                ),
                eventType="event",
            )
        self.__previousVisible = self.visible


class TemplateQuestion(models.Model):
    """
    Stores template questions for signup forms as JSON format. Used in signup form creation.
    """

    class Meta:
        verbose_name = _("Template question")
        verbose_name_plural = _("Template questions")

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    questions = JSONField()
    deleted = models.BooleanField(default=False)

    def __str__(self):
        return _("Template questions: {}").format(self.name)


class SignupForm(models.Model):
    """Model for event signup form. Stores questions in JSON format."""

    class Meta:
        verbose_name = _("Signup form")
        verbose_name_plural = _("Signup forms")

    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=255)
    start_time = models.DateTimeField(default=timezone.now)
    end_time = models.DateTimeField(default=timezone.now)
    questions = JSONField()
    schema = JSONField()
    visible = models.BooleanField(default=True)
    quota = models.PositiveIntegerField(blank=True, null=True)
    email_content = models.TextField(blank=True)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        delete_str = _("Deleted: ") if self.deleted else ""
        return _("#{} {}{}").format(self.id, delete_str, self.title)

    @property
    def signups(self):
        return Signup.objects.filter(signupForm=self, deleted=False).order_by("pk")

    @property
    def isOpen(self):
        now = timezone.now()
        return self.start_time <= now and now < self.end_time


class Signup(models.Model):
    """
    Actual signup into any SignupForm. Deletes are soft.
    """

    class Meta:
        verbose_name = _("Sign-up")
        verbose_name_plural = _("Sign-ups")

    id = models.AutoField(primary_key=True)
    signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
    time = models.DateTimeField(default=timezone.now)
    answer = JSONField()
    # Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
    list_name = models.CharField(_("Name"), max_length=255)
    # If there is email in questions, we save it as own field
    email = models.EmailField(blank=True, null=True)
    # Random unique identifier. Used for signup editing by the user.
    uuid = models.UUIDField(default=uuid4, editable=False)
    # Random unique identifier generated by browser upon opening signup form. Used to prevent duplicate signups.
    submit_id = models.UUIDField(null=True, editable=False, unique=True)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        delete_str = _("Deleted: ") if self.deleted else ""
        return f"{self.signupForm}: {delete_str}{self.list_name} ({self.pk})"


@receiver(post_save, sender=Signup)
def email_on_signup(sender, instance, created, **kwargs):
    if created and instance.email:
        # TODO: Possible bug due to many-to-many relationship with events and forms.
        # TODO: Subject field crashes with lazy loaded translations.
        try:
            # subject = _(f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}")
            subject = f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}"
        except AttributeError:
            # subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}")
            subject = f"Olet ilmoittautunut ilmoon {instance.signupForm.title}"
        send_signup_email(
            instance.email,
            subject,
            instance.id,
            instance.uuid,
            instance.signupForm.email_content,
        )


class JobAd(models.Model):
    """Job advertisements shown on Corporate relations page"""

    class Meta:
        verbose_name = _("JobAd")
        verbose_name_plural = _("JobAds")

    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    content = models.TextField()
    visible = models.BooleanField(default=True)
    created_at = models.DateTimeField(default=timezone.now)
    autohide_at = models.DateTimeField(default=month_from_now)
    autohide_enabled = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)

    def __str__(self):
        delete_str = _("Deleted: ") if self.deleted else ""
        return f"{delete_str}{self.title}"

    __previousVisible = False

    def __init__(self, *args, **kwargs):
        super(JobAd, self).__init__(*args, **kwargs)
        self.__previousVisible = self.visible

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        created = self.pk is None
        super(JobAd, self).save(force_insert, force_update, *args, **kwargs)

        if self.visible and (created or not self.__previousVisible):
            self.refresh_from_db()  # Fetch so we can use primary key
            url = f"https://{FRONTEND_URL}/jobads/{self.pk}"
            processHooks(
                message=generateMessage(
                    "Uusi työpaikkailmoitus", self.title, self.description, url
                ),
                eventType="jobad",
            )
        self.__previousVisible = self.visible


def generateMessage(heading: str, title: str, description: str, url: str):
    return render_to_string(
        "webapp/tg_message.tpl",
        {"heading": heading, "title": title, "description": description, "url": url},
    )


def processHooks(message: str, eventType: str):
    allHooks = BaseWebhook.objects.all()
    for hook in list(allHooks):
        if hook.plugs[eventType] is True:
            hook.broadcast(message)


class BaseWebhook(PolymorphicModel):
    """Webhook base class instance"""

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    url = (
        models.URLField()
    )  # URL where webhook message is broadcast. For example Telegram webhook API
    # "Plugs"; which notifications are sent to this specific webhook instance
    kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False)
    ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"), default=False)
    feed_published = models.BooleanField(_("Hook published news"), default=False)
    jobad_published = models.BooleanField(_("Hook published Job Ads"), default=False)
    event_published = models.BooleanField(_("Hook published events"), default=False)
    signup_opened = models.BooleanField(_("Hook opened signups"), default=False)

    @property
    def plugs(self):
        return {
            "kaehmy": self.kaehmy_submit,
            "ohlhafv": self.ohlhafv_submit,
            "feed": self.feed_published,
            "jobad": self.jobad_published,
            "event": self.event_published,
            "signup": self.signup_opened,
        }

    def parseData(self):
        pass

    def broadcast(self, message):
        resp = requests.post(self.url, json=self.parseData(message))
        logging.debug(f"Webhook API response: HTTP{resp.status_code}")
        logging.debug(resp.content)


class GenericWebhook(BaseWebhook):
    class Meta:
        proxy = True
        verbose_name = _("Webhook")
        verbose_name_plural = _("Webhooks")

    def __str__(self):
        return 'Webhook "{}"'.format(self.name)

    def parseData(self, message):
        return {"text": message}


class TelegramHook(BaseWebhook):
    """Model containing the channel id of a Telegram chat"""

    class Meta:
        verbose_name = _("Telegram channel")
        verbose_name_plural = _("Telegram channels")

    channel_id = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return 'Telegram channel: "{}"'.format(self.name)

    def parseData(self, message):
        return {"text": message, "chat_id": self.channel_id, "parse_mode": "Markdown"}


auditlog.register(Tag)
auditlog.register(Feed)
auditlog.register(Event)
auditlog.register(SignupForm)
auditlog.register(Signup)
auditlog.register(JobAd)
auditlog.register(GenericWebhook)
auditlog.register(TelegramHook)
