from django.db import models
from django.conf import settings
from django.utils import timezone
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.exceptions import InvalidSignature
import base64
import binascii
from django.core.exceptions import ValidationError


class InvitationManager(models.Manager):

    def get_invitation_by_hash(self, msg_hash):
        invitations = self.filter(msg_hash=msg_hash)
        if len(invitations) > 0:
            return invitations.first()
        return None


class Invitation(models.Model):

    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    msg = models.CharField(max_length=250)
    msg_hash = models.CharField(max_length=250)
    created_date = models.DateTimeField(default=timezone.now)
    consumed = models.BooleanField(default=False)
    guest = models.ForeignKey(settings.AUTH_USER_MODEL,
                              related_name='guests',
                              null=True)

    objects = InvitationManager()

    def save(self, *args, **kwargs):
        if self.msg_hash == '':
            raise ValidationError('msg_hash cannot be empty.')
        super(Invitation, self).save(*args, **kwargs)

    @classmethod
    def _create_HMAC_SHA256_hash(cls, message):
        key = settings.SECRET_KEY.encode()
        h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
        h.update(message.encode())
        urlsafe_hash = base64.urlsafe_b64encode(h.finalize())
        return urlsafe_hash.decode()

    @classmethod
    def _validate_HMAC_SHA256_hash(cls, urlsafe_hash, message):
        key = settings.SECRET_KEY.encode()
        h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
        h.update(message.encode())
        try:
            h.verify(base64.urlsafe_b64decode(urlsafe_hash.encode()))
        except (InvalidSignature, binascii.Error):
            return False
        else:
            return True

    @classmethod
    def create(cls, user):
        inv = cls(owner=user)
        message = '{user}::{time}'.format(
            user=user.username,
            time=timezone.now()
        )
        inv.msg_hash = cls._create_HMAC_SHA256_hash(message)
        inv.msg = message
        return inv

    def validate(self, urlsafe_hash):
        return self._validate_HMAC_SHA256_hash(urlsafe_hash, self.msg)

    def expired(self):
        now = timezone.now()
        return (now - self.created_date).total_seconds() > 60 * 60 * 24
