# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
#
from datetime import timedelta
import uuid
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.signals import pre_delete
from django.utils import timezone
from catalogue.models import Book, Tag


class Deleted(models.Model):
    object_id = models.IntegerField()
    slug = models.SlugField('slug', max_length=120, blank=True, db_index=True)
    content_type = models.ForeignKey(ContentType, models.CASCADE)
    category = models.CharField(max_length=64, null=True, blank=True, db_index=True)
    created_at = models.DateTimeField(editable=False, db_index=True)
    deleted_at = models.DateTimeField(auto_now_add=True, db_index=True)

    class Meta:
        unique_together = (('content_type', 'object_id'),)


def _pre_delete_handler(sender, instance, **kwargs):
    """ save deleted objects for change history purposes """

    if sender in (Book, Tag):
        if sender == Tag:
            if instance.category in ('book', 'set'):
                return
            category = instance.category
        else:
            category = None
        content_type = ContentType.objects.get_for_model(sender)
        Deleted.objects.create(
            content_type=content_type,
            object_id=instance.id,
            created_at=instance.created_at,
            category=category,
            slug=instance.slug
        )
pre_delete.connect(_pre_delete_handler)


class BookUserData(models.Model):
    book = models.ForeignKey(Book, models.CASCADE)
    user = models.ForeignKey(User, models.CASCADE)
    complete = models.BooleanField(default=False)
    last_changed = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = [('user', 'book')]

    @property
    def state(self):
        return 'complete' if self.complete else 'reading'

    @classmethod
    def update(cls, book, user, state):
        instance, created = cls.objects.get_or_create(book=book, user=user)
        instance.complete = state == 'complete'
        instance.save()
        return instance


KEY_SIZE = 18
SECRET_SIZE = 32

CONSUMER_STATES = (
    ('pending', 'Pending approval'),
    ('accepted', 'Accepted'),
    ('canceled', 'Canceled'),
)


class Nonce(models.Model):
    token_key = models.CharField(max_length=KEY_SIZE)
    consumer_key = models.CharField(max_length=KEY_SIZE)
    key = models.CharField(max_length=255)

    def __str__(self):
        return "Nonce %s for %s" % (self.key, self.consumer_key)


class Consumer(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    key = models.CharField(max_length=KEY_SIZE)
    secret = models.CharField(max_length=SECRET_SIZE)
    status = models.CharField(max_length=16, choices=CONSUMER_STATES, default='pending')
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, models.CASCADE,
        null=True, blank=True, related_name='consumers'
    )

    def __str__(self):
        return "Consumer %s with key %s" % (self.name, self.key)


class Token(models.Model):
    REQUEST = 1
    ACCESS = 2
    REFRESH = 3
    TOKEN_TYPES = (
        (REQUEST, 'Request'),
        (ACCESS, 'Access'),
        (REFRESH, 'Refresh')
    )

    key = models.CharField(max_length=KEY_SIZE)
    secret = models.CharField(max_length=SECRET_SIZE)
    token_type = models.IntegerField(choices=TOKEN_TYPES)
    timestamp = models.IntegerField()
    is_approved = models.BooleanField(default=False)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, models.CASCADE,
        null=True, blank=True, related_name='tokens'
    )
    consumer = models.ForeignKey(Consumer, models.CASCADE, null=True, blank=True)

    def __str__(self):
        return "%s Token %s for %s" % (self.get_token_type_display(), self.key, self.consumer)


class SessionTransferToken(models.Model):
    token = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()
    used = models.BooleanField(default=False)

    @classmethod
    def create_for_user(cls, user, lifetime_seconds=30):
        return cls.objects.create(
            user=user,
            expires_at=timezone.now() + timedelta(seconds=lifetime_seconds)
        )

    def is_valid(self):
        if self.used:
            return False
        if timezone.now() > self.expires_at:
            return False
        return True
