"""Webapp app models."""

from django.conf import settings
from django.db import models
from django.utils import timezone
# from datetime import timedelta
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from webapp.utils import month_from_now, send_signup_email
from django.utils.translation import ugettext_lazy as _
from auditlog.registry import auditlog
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.postgres.fields import JSONField
from uuid import uuid4
import logging
from smtplib import SMTPAuthenticationError

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."""

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

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

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


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

    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()


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

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

    def __str__(self):
        return _('Feed: {}').format(self.title)

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


class Event(BaseFeed):
    """Model for event."""

    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)

    def __str__(self):
        return _('Event: {}').format(self.title)

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


class TemplateQuestion(models.Model):
    """Stores template questions for signup forms as JSONB"""
    name = models.CharField(max_length=255)
    question = JSONField()

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

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


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

    title = models.CharField(max_length=255)
    start_time = models.DateTimeField(default=timezone.now)
    end_time = models.DateTimeField(default=timezone.now)
    questions = JSONField()
    visible = models.BooleanField(default=True)

    def __str__(self):
        return _('#{} {}').format(self.id, self.title)

    @property
    def signups(self):
        return Signup.objects.filter(signupForm=self)

    @property
    def schema(self):
        questions = self.questions
        properties = {}

        for q in questions:
            id = q["id"]
            question_type = q["type"]
            if question_type == "text":
                properties[id] = {
                    "type": "string"
                }
            elif question_type == "email":
                # Format is just a "FYI" field, so we also have pattern.
                properties[id] = {
                    "type": "string",
                    "format": "email",
                    "pattern": EMAIL_REGEX
                }
            elif question_type == "radiobutton":
                options = q["options"]
                regexes = map(lambda x: f"^{x}$", options)
                pattern = "|".join(regexes)
                properties[id] = {
                    "type": "string",
                    # Remove last regex or
                    "pattern": pattern,

                }
            elif question_type == "checkbox":
                options = q["options"]
                regexes = map(lambda x: f"^{x}$", options)
                pattern = "|".join(regexes)

                properties[id] = {
                    "type": "array",
                    "uniqueItems": True,
                    "maxItems": len(options),
                    "items": {
                        "type": "string",
                        "pattern": pattern
                    }
                }
            else:
                raise Exception("invalid question type!")

        ids = list(map(lambda x: x["id"], questions))
        return {
            "type": "object",
            "required": ids,
            "minProperties": len(ids),
            "maxProperties": len(ids),
            "properties": properties
        }

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


class Signup(models.Model):
    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)

    def __str__(self):
        return f"{self.signupForm}: {self.list_name} ({self.pk})"

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


@receiver(post_save, sender=Signup)
def email_on_singup(sender, instance, created, **kwargs):
    """Send email validation."""
    if not settings.ENABLE_AUTOMATIC_EMAILS:
        return

    try:
        if created and instance.email:
            # TODO: Possible bug due to many-to-many relationship with events and forms.
            subject = _(f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}")
            send_signup_email(instance.email, subject, instance.id, instance.uuid)
    except SMTPAuthenticationError:
        logging.error('Failed to send email to signup')


class BaseRole(models.Model):
    """Base model for occupations/roles."""

    name = models.CharField(_('Name'), max_length=255)
    is_board = models.BooleanField(_('Board member'))

    def __str__(self):
        n = self.name.capitalize()
        return '{} ({})'.format(n, _('board member')) if self.is_board else n


class PresetRole(BaseRole):
    """Model representing a preset occupation in the guild."""

    description = models.TextField(_('Description'))


class Committee(models.Model):
    """
    Committee model
    Has many Roles found under variable roles
    """

    class Meta:
        """Meta class for Committee class."""

        verbose_name = _('Committee')
        verbose_name_plural = _('Committees')

    def __str__(self):
        return _('Committee: {}').format(self.name)

    name = models.CharField(_("Name"), max_length=255)

    @property
    def current_roles(self):
        return self.roles.all().filter(end_date__gte=timezone.now()).filter(start_date__lte=timezone.now())


class Role(PresetRole):
    """
    Model for Role.

    Model representing an active or historical occupation
    in the guild.
    """

    class Meta:
        """Meta class for Role model."""

        verbose_name = _('Role')
        verbose_name_plural = _('Roles')

    committee = models.ForeignKey('Committee', related_name='roles', on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return '{} (Hallitus: {}) ({})'.format(self.name, _("Yes") if self.is_board else _("No"), self.committee)


class Occupation(models.Model):
    """
    Model for a occupation in guild.

    Model links Official into a role he/she has or has had in the guild.
    """

    class Meta:
        verbose_name = _('Occupation')
        verbose_name_plural = _('Occupations')

    start_date = models.DateField(_('Start date'))
    end_date = models.DateField(_('End date'))
    role = models.ForeignKey('Role', on_delete=models.SET_NULL, null=True)

    @staticmethod
    def by_year(year):
        return Occupation.objects.filter(
            end_date__gte=timezone.datetime(year, 1, 1)).filter(
            start_date__lte=timezone.datetime(year, 12, 31))

    def __str__(self):
        return '{}: {} - {}'.format(self.role.name, self.start_date, self.end_date)


class Official(models.Model):
    """Model representing a guild official."""

    class Meta:
        """Meta class for Official class."""

        verbose_name = _('Official')
        verbose_name_plural = _('Officials')

    user = models.OneToOneField(User, on_delete=models.CASCADE)

    first_name = models.CharField(_('First name'), max_length=30)
    last_name = models.CharField(_('Last name'), max_length=150)
    email = models.EmailField(_('Email address'))
    phone_number = PhoneNumberField(_('Phone number'))
    role_history = models.ManyToManyField('Occupation', 'officials', blank=True)
    image = models.ImageField(blank=True, null=True)

    @staticmethod
    def by_year(year):
        return Official.objects.filter(
            role_history__in=Occupation.occupations_by_year(year)).distinct()

    def __str__(self):
        return '{} {}'.format(self.first_name, self.last_name)


@receiver(post_save, sender=Official)
def save_user_official(sender, instance, **kwargs):
    instance.user.first_name = instance.first_name
    instance.user.last_name = instance.last_name
    instance.user.email = instance.email
    instance.user.save()


auditlog.register(Tag)
auditlog.register(Feed)
auditlog.register(Event)
auditlog.register(Signup)
auditlog.register(PresetRole)
auditlog.register(Role)
auditlog.register(Official)
