from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes import generic
from request_provider.signals import get_request
from django.core.validators import MinLengthValidator
from django.contrib.contenttypes.models import ContentType

from history.models import HistoryEntry


# the very base class for our models which provides a couple of common functionality
# and properties.
class WinoseBaseModel(models.Model):
    class Meta:
        abstract = True

    # a model itself defines the names for its routing
    overview_url = None
    detail_url = None
    update_url = None
    create_url = None

    # instead of deleting things, de/reactive
    is_active = models.BooleanField(
        default=True,
        editable=False  # don't list in forms
    )

    # override to allow cascading activation toggling (e.g. sample -> bag)
    # caller needs to care to wrap this ideally into a transaction, else
    # things might get slow
    def toggle_is_active(self, state=None):
        if state is None:
            self.is_active = not self.is_active
        else:
            self.is_active = state
        self.save()

    # allow easier verbose name lookup in templates, especially
    # for related objects via fk relations
    def verbose_name(self):
        return self._meta.verbose_name

    def verbose_name_plural(self):
        return self._meta.verbose_name_plural


# the extended base class provides history tracking, this distinction is made
# to have an easier time with models where history tracking isn't wanted or even 'good'
class WinoseBaseWithTrackingModel(WinoseBaseModel):
    class Meta:
        abstract = True

    # faked foreignkey to the history of this object
    # this content based foreign key allows to point to any child of this base class
    change_set = generic.GenericRelation(HistoryEntry)

    # overriding save to track changes for history tracking
    def save(self, *args, **kwargs):
        user = None
        # TODO: crappy performance
        if 'rest_user' in kwargs:
            user = kwargs['rest_user']
            kwargs.pop('rest_user')
        elif get_request():
            user = get_request().user

        # object exists, so it is a change
        if self.pk:
            old = self.__class__._default_manager.get(pk=self.pk)
            for field in self._meta.fields:
                # skip certain auto updated fields we don't want to track
                if field.name in HistoryEntry.no_tracking():
                    continue

                field_name = field.name
                # verbose_field_name = field.verbose_name.encode('utf8')

                old_val = getattr(old, field_name)
                new_val = getattr(self, field_name)

                if old_val != new_val:  # if there actually was a change
                    # store a new history entry for that change
                    change = HistoryEntry()

                    # convert null data for non string fields
                    # into empty strings, else the not null constraint is broken
                    if not old_val:
                        old_val = ''
                    if not new_val:
                        new_val = ''

                    change.old_value = old_val
                    change.new_value = new_val
                    change.who = user
                    change.entity = self
                    # determine verbose (and localized) field name during actual lookup using this
                    # if this fails just display unknown field or something
                    change.field_name = field_name
                    change.save()
                else:
                    pass
        else:
            # new object
            # need to save it first, else referring to it won't work, obviously
            super(WinoseBaseWithTrackingModel, self).save(*args, **kwargs)
            # create history entry for the new thingy
            cs = HistoryEntry()
            cs.who = user
            cs.entity = self
            cs.save()

        super(WinoseBaseWithTrackingModel, self).save(*args, **kwargs)

    # convenience squared, don't ask.
    @property
    def user_list(self):
        from users.models import WinoseUser

        return WinoseUser.objects.all()

    @staticmethod
    def verbose_for_computed(field_name):
        if field_name == 'qualified':
            return _('qualified')
        elif field_name == 'total_dilution':
            return _('total pre-dilution')
        else:
            return _('unknown')

    @property
    def last_changed_at(self):
        return HistoryEntry.objects.filter(content_type=ContentType.objects.get_for_model(self._meta.model),
                                           object_id=self.pk).order_by('timestamp').first().timestamp

# base model for contact information
class ContactDataBase(models.Model):
    class Meta:
        abstract = True

    email = models.EmailField(
        max_length=127,
        blank=True,
        verbose_name=_('E-mail')
    )

    phone = models.CharField(
        max_length=63,
        blank=True,
        verbose_name=_('Phone')
    )

    mobile = models.CharField(
        max_length=63,
        blank=True,
        verbose_name=_('Mobile')
    )


# abstract base for a person
class PersonBase(models.Model):
    class Meta:
        abstract = True

    GENDERS = (('U', _('unknown')),
               ('M', _('male')),
               ('F', _('female')),
               ('N', _('not applicable')))

    # everyone needs a first name, isn't it
    # this has also plenty of space to store middle names
    first_name = models.CharField(
        max_length=127,
        verbose_name=_('first name'),
        validators=[MinLengthValidator(2)]
    )

    # and a last name
    last_name = models.CharField(
        max_length=63,
        verbose_name=_('last name'),
        validators=[MinLengthValidator(2)]
    )

    gender = models.CharField(
        max_length=1,
        choices=GENDERS,
        default='U',
        verbose_name=_('gender')
    )


# abstract base for an address
class AddressBase(models.Model):
    class Meta:
        abstract = True

    # street plus number
    address = models.CharField(
        max_length=127,
        verbose_name=_('address')
    )

    zip = models.CharField(
        max_length=63,
        verbose_name=_('postal code')
    )

    city = models.CharField(
        max_length=63,
        verbose_name=_('city')
    )

    country = models.CharField(
        max_length=63,
        verbose_name=_('country')
    )

    # optional parts

    state = models.CharField(
        max_length=63,
        verbose_name=_('state/district'),
        blank=True
    )

    address2 = models.CharField(
        max_length=127,
        verbose_name=_('address supplements'),
        blank=True
    )
