"""Notification app models."""

from django.conf import settings
from django.db import models
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _

from account.models import User
from location.models import Country
from notification.tasks import send_unsubscribe_email
from utils.methods import generate_string_code
from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin


class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin):
    index_name = models.CharField(max_length=255, verbose_name=_('Index name'), unique=True)
    name = TJSONField(blank=True, null=True, default=None,
                      verbose_name=_('name'),
                      help_text='{"en-GB":"some text"}')
    country = models.ForeignKey(Country, on_delete=models.PROTECT,
                                blank=True, null=True, default=None,
                                verbose_name=_('country'))


# todo: associate user & subscriber after users registration
class SubscriberManager(models.Manager):
    """Extended manager for Subscriber model."""

    def make_subscriber(self, email=None, user=None, ip_address=None, country_code=None,
                        locale=None, subscription_types=None, *args, **kwargs):
        """Make subscriber and update info."""
        # search existing object
        if not user:
            user = User.objects.filter(email=email).first()

        if user:
            obj = self.model.objects.filter(models.Q(user=user) | models.Q(email=user.email)).first()
        else:
            obj = self.model.objects.filter(email=email).first()

        # update or create
        if obj:
            if user:
                obj.user = user
                obj.email = None

            else:
                obj.email = email

            obj.ip_address = ip_address
            obj.country_code = country_code
            obj.locale = locale
            obj.update_code = generate_string_code()
            obj.save()

        else:
            obj = self.model.objects.create(
                user=user, email=email, ip_address=ip_address,
                country_code=country_code, locale=locale
            )

        if subscription_types is not None:
            obj.subscription_types.set(subscription_types)
            obj.subscribe_set.update(unsubscribe_date=None)

        return obj

    def associate_user(self, user):
        """Associate user."""
        obj = self.model.objects.filter(user=user).first()

        if obj is None:
            obj = self.model.objects.filter(email=user.email_confirmed, user__isnull=True).first()

            if obj:
                obj.user = user
                obj.email = None
                obj.save()

        return obj


class Subscriber(ProjectBaseMixin):
    """Subscriber model."""

    user = models.ForeignKey(
        User,
        blank=True,
        null=True,
        default=None,
        on_delete=models.SET_NULL,
        related_name='subscriber',
        verbose_name=_('User'),
    )
    email = models.EmailField(blank=True, null=True, default=None, unique=True, verbose_name=_('Email'))
    ip_address = models.GenericIPAddressField(blank=True, null=True, default=None, verbose_name=_('IP address'))
    country_code = models.CharField(max_length=10, blank=True, null=True, default=None, verbose_name=_('Country code'))
    locale = models.CharField(blank=True, null=True, default=None, max_length=10, verbose_name=_('Locale identifier'))
    update_code = models.CharField(
        max_length=254,
        blank=True,
        null=True,
        default=None,
        db_index=True,
        verbose_name=_('Token'),
    )
    old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)

    subscription_types = models.ManyToManyField(SubscriptionType, through='Subscribe')

    objects = SubscriberManager()

    class Meta:
        """Meta class."""

        verbose_name = _('Subscriber')
        verbose_name_plural = _('Subscribers')

    def save(self, *args, **kwargs):
        """Overrided save method."""
        if self.update_code is None:
            self.update_code = generate_string_code()
        return super(Subscriber, self).save(*args, **kwargs)

    def unsubscribe(self, query: dict):
        """Unsubscribe user."""
        self.subscribe_set.update(unsubscribe_date=now())

        if settings.USE_CELERY:
            send_unsubscribe_email.delay(self.pk)
        else:
            send_unsubscribe_email(self.pk)

    @property
    def send_to(self):
        """Actual email."""
        return self.user.email if self.user else self.email

    @property
    def link_to_unsubscribe(self):
        """Link to unsubscribe."""
        schema = settings.SCHEMA_URI
        site_domain = settings.SITE_DOMAIN_URI
        url = settings.SITE_REDIRECT_URL_UNSUBSCRIBE
        query = f'?code={self.update_code}'
        return f'{schema}://{site_domain}{url}{query}'

    @property
    def active_subscriptions(self):
        return self.subscription_types.exclude(subscriber__subscribe__unsubscribe_date__isnull=False)


class SubscribeQuerySet(models.QuerySet):

    def active(self, switcher=True):
        """Fetches active subscriptions."""
        return self.exclude(unsubscribe_date__isnull=not switcher)


class Subscribe(ProjectBaseMixin):
    """Subscribe model."""

    subscribe_date = models.DateTimeField(_('Last subscribe date'), blank=True, null=True, default=now)
    unsubscribe_date = models.DateTimeField(_('Last unsubscribe date'), blank=True, null=True, default=None)

    subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE)
    subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE)

    objects = SubscribeQuerySet.as_manager()

    class Meta:
        """Meta class."""

        verbose_name = _('Subscribe')
        verbose_name_plural = _('Subscribes')
