# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models
from polymorphic import PolymorphicModel
from coin.offers.models import OfferSubscription
from django.db.models.signals import post_save, post_delete
from django.core.exceptions import ObjectDoesNotExist
from django.dispatch import receiver

from coin.resources.models import IPSubnet

"""
Implementation note : Configuration is a PolymorphicModel.
The childs of Configuration are the differents models to store
technical informations of a subscription.

To add a new configuration backend, you have to create a new app with a model
which inherit from Configuration.
Your model can implement Meta verbose_name to have human readable name and a 
url_namespace variable to specify the url namespace used by this model.
"""

class Configuration(PolymorphicModel):

    offersubscription = models.OneToOneField(OfferSubscription,
                                             related_name='configuration',
                                             verbose_name='abonnement')
    comment = models.CharField(blank=True, max_length=512,
                               verbose_name="commentaire")

    @staticmethod
    def get_configurations_choices_list():
        """
        Génère automatiquement la liste de choix possibles de configurations
        en fonction des classes enfants de Configuration
        """
        return tuple((x().__class__.__name__,x()._meta.verbose_name) 
            for x in Configuration.__subclasses__())
    
    def model_name(self):
        return self.__class__.__name__
    model_name.short_description = 'Nom du modèle'

    def configuration_type_name(self):
        return self._meta.verbose_name
    configuration_type_name.short_description = 'Type'

    def get_absolute_url(self):
        """
        Renvoi l'URL d'accès à la page "details" de l'objet
        Une url doit être nommée "details"
        """
        from django.core.urlresolvers import reverse
        return reverse('%s:details' % self.get_url_namespace(), 
                       args=[str(self.id)])

    def get_url_namespace(self):
        """
        Renvoi le namespace utilisé par la configuration. Utilise en priorité
        celui définit dans la classe enfant dans url_namespace sinon
        par défaut utilise le nom de la classe en minuscule
        """
        if self.url_namespace:
            return self.url_namespace
        else:
            return self.model_name().lower()

    class Meta:
        verbose_name = 'configuration'


@receiver(post_save, sender=IPSubnet)
def subnet_save_event(sender, **kwargs):
    """Fires when a subnet is saved (created/modified).  We tell the
    configuration backend to do whatever it needs to do with it.

    We should use a pre_save signal, so that if anything goes wrong in the
    backend (exception raised), nothing is actually saved in the database.
    But it has a big problem: the configuration backend will not see the
    change, since it has not been saved into the database yet.

    That's why we use a post_save signal instead.  But surprisingly, all
    is well: if we raise an exception here, the IPSubnet object will not
    be saved in the database.  But the backend *does* see the new state of
    the database.  It looks like the database rollbacks if an exception is
    raised.  Whatever the reason, this is not a documented feature of
    Django signals.
    """
    subnet = kwargs['instance']
    try:
        config = subnet.configuration
        config.save_subnet(subnet, kwargs['created'])
    except ObjectDoesNotExist:
        pass


@receiver(post_delete, sender=IPSubnet)
def subnet_delete_event(sender, **kwargs):
    """Fires when a subnet is deleted.  We tell the configuration backend to
    do whatever it needs to do with it.
    """
    subnet = kwargs['instance']
    try:
        config = subnet.configuration
        config.delete_subnet(subnet)
    except ObjectDoesNotExist:
        pass
