from choicesenum import ChoicesEnum
from django.conf import settings
from django.db import models
from django.utils import timezone
import strictyaml


class Legacy(ChoicesEnum):
    UNDEFINED = 0, "Undefined"


def create_missing_user_limits(user):

    for code in settings.LM_SERVICES.MAP.keys():
        is_defined = code != settings.LM_SERVICES.UNDEFINED
        is_exists = Limit.objects.filter(user=user, service=code).exists()
        if is_defined and not is_exists:
            Limit(user=user, service=code).save()


class Limit(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    service = models.IntegerField(default=settings.LM_SERVICES.UNDEFINED,
                                  choices=settings.LM_SERVICES.choices())
    renewal_date = models.DateTimeField(default=None, blank=True, null=True)
    expiry_date = models.DateTimeField(default=None, blank=True, null=True)
    volume_total = models.DecimalField(default=0, decimal_places=2, max_digits=6)
    time_total = models.DecimalField(default=0, decimal_places=2, max_digits=6)
    is_active = models.BooleanField(default=False)
    created_date = models.DateTimeField(default=timezone.now)
    updated_date = models.DateTimeField(default=timezone.now)

    def service_label(self):

        return settings.LM_SERVICES.get_name_by_code(self.service)

    def active_label(self):

        if self.is_enabled():
            return "Yes"
        else:
            return "No"

    def endpoint_short_label(self):

        return self.endpoint_label("%Y-%m-%d")

    def endpoint_full_label(self):

        return self.endpoint_label("%Y-%m-%d %H:%M:%S %z")

    def endpoint_label(self, label_format):

        is_blank = self.renewal_date is None and self.expiry_date is None
        is_renewal = self.renewal_date is not None and self.expiry_date is None
        is_expiry = self.renewal_date is None and self.expiry_date is not None

        if is_blank:
            return "-"
        elif is_renewal:
            return self.renewal_date.strftime(label_format)
        elif is_expiry:
            return self.expiry_date.strftime(label_format)
        else:
            return "Invalid"

    def credit_label(self):

        if self.service == settings.LM_SERVICES.TUNNEL:
            return self.days_credit_label()
        else:
            return self.days_credit_label()

    def timedelta_to_daystring(self, delta):

        single_day = 60 * 60 * 24
        days = int(round(delta.total_seconds() / single_day))

        if days <= 62:
            return str(days) + " days"
        elif days <= 744:
            return str(days // 30) + " months"
        else:
            return str(days // 365) + " years"

    def days_credit_label(self):

        time_delta = self.remaining_use_time()
        if time_delta.total_seconds() == 0:
            label = "-"
        elif time_delta.total_seconds() > 0:
            label = self.timedelta_to_daystring(time_delta)
        else:
            label = self.timedelta_to_daystring(timezone.timedelta(0))
        return label

    def remaining_use_time(self):
        """Returns the time delta left before renewal date or expiration
        time. If the limit is measured in time credit it returns
        time_total in deltatime.

        """
        has_credit_only = (self.expiry_date is None
                           and self.renewal_date is None
                           and self.time_total > 0)
        has_valid_expiry = (self.expiry_date is not None
                            and self.renewal_date is None
                            and self.time_total == 0)
        has_valid_renewal = (self.expiry_date is None
                             and self.renewal_date is not None
                             and self.time_total == 0)
        now = timezone.now()

        if has_credit_only:
            return timezone.timedelta(self.time_total)
        elif has_valid_expiry:
            return self.expiry_date - now
        elif has_valid_renewal:
            return self.renewal_date - now
        else:
            return timezone.timedelta(0)

    def service_activable_by_user(self):
        """Returns True when the user is allowed to activate the
        service. Returns False when the user is NOT allowed to
        activate the service.
        """
        if (
            self.remaining_use_time().total_seconds() > 0
            and self.service == settings.LM_SERVICES.TUNNEL
        ):
            return True
        return False

    def enable(self, ren_date):
        if self.service == settings.LM_SERVICES.TUNNEL:
            # TUNNEL special case. It requires user manual activation
            # the first time to trigger tunnel_account creation
            if not self.user.has_tunnel_account():
                self.is_active = False
        elif self.service == settings.LM_SERVICES.CHAT:
            self.user.set_ldap_group("chat")
            self.is_active = True
        elif self.service == settings.LM_SERVICES.MAIL:
            self.user.set_ldap_group("mail")
            self.is_active = True
        elif self.service == settings.LM_SERVICES.SOCIAL:
            self.user.set_ldap_group("social")
            self.is_active = True
        elif self.service == settings.LM_SERVICES.XMPP:
            self.user.set_ldap_group("xmpp")
            self.is_active = True
        elif self.service == settings.LM_SERVICES.GROUP:
            self.is_active = True
        else:
            raise Exception(
                "Invalid attempt to enable. Service not recognised.")
        self.renewal_date = ren_date

    def disable(self):
        if self.service == settings.LM_SERVICES.TUNNEL:
            self.user.terminate_tunnel_account()
            self.is_active = False
        elif self.service == settings.LM_SERVICES.CHAT:
            self.user.remove_ldap_group("chat")
            self.is_active = False
        elif self.service == settings.LM_SERVICES.MAIL:
            self.user.remove_ldap_group("mail")
            self.is_active = False
        elif self.service == settings.LM_SERVICES.SOCIAL:
            self.user.remove_ldap_group("social")
            self.is_active = False
        elif self.service == settings.LM_SERVICES.XMPP:
            self.user.remove_ldap_group("xmpp")
            self.is_active = False
        elif self.service == settings.LM_SERVICES.GROUP:
            self.is_active = False
        else:
            raise Exception(
                "Invalid attempt to disable. Service not recognised.")
        self.renewal_date = None

    def is_enabled(self):
        """If the limit is not TUNNEL returns the value of is_acitve. For
        TUNNEL limit returns True if there is time remaining to use
        the service.
        """
        if self.service == settings.LM_SERVICES.TUNNEL:
            return self.remaining_use_time().total_seconds() > 0
        return self.is_active


class ExternalBundle(models.Model):
    """Class used to represent external bundles that exist in external
    entities like WooCommerce
    """
    parser = models.IntegerField(default=settings.LM_PARSERS.UNDEFINED,
                                 choices=settings.LM_PARSERS.choices())
    external_key = models.CharField(max_length=30)
    service = models.IntegerField(default=settings.LM_SERVICES.UNDEFINED,
                                  choices=settings.LM_SERVICES.choices())
    time_credit = models.DecimalField(default=0, decimal_places=2, max_digits=6)
    volume_credit = models.DecimalField(default=0, decimal_places=2, max_digits=6)
    created_date = models.DateTimeField(default=timezone.now)
    updated_date = models.DateTimeField(default=timezone.now)


def get_available_bundles():

    with open(settings.WOO_BUNDLES_INFO_FILE, 'r') as stream:
        bundles = strictyaml.load(stream.read()).data
    return bundles
