import jwt

from datetime import datetime
from datetime import timedelta

from django.conf import settings
from django.db import models
from django.core import validators
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.models import BaseUserManager

from secrets import token_urlsafe


# Create your models here
class StudyGroup(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.TextField(max_length=30, verbose_name='Название группы')

    class Meta:
        verbose_name_plural = 'Учебная группа'
        verbose_name = 'Учебные группы'

    def __str__(self):
        return self.name


class Role(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.TextField(max_length=30, verbose_name='Роль')

    class Meta:
        verbose_name_plural = 'Роль'
        verbose_name = 'Роли'

    def __str__(self):
        return self.name


class UserManager(BaseUserManager):
    """
    Django требует, чтобы пользовательские `User`
    определяли свой собственный класс Manager.
    Унаследовав от BaseUserManager, мы получаем много кода,
    используемого Django для создания `User`.

    Все, что нам нужно сделать, это переопределить функцию
    `create_user`, которую мы будем использовать
    для создания объектов `User`.
    """

    def _create_user(self, username, email, password=None, **extra_fields):
        if not username:
            raise ValueError('Указанное имя пользователя должно быть установлено')

        if not email:
            raise ValueError('Данный адрес электронной почты должен быть установлен')

        email = self.normalize_email(email)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_user(self, username, email, password=None, **extra_fields):
        """
        Создает и возвращает `User` с адресом электронной почты,
        именем пользователя и паролем.
        """
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        """
        Создает и возвращает пользователя с правами
        суперпользователя (администратора).
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Суперпользователь должен иметь is_staff=True.')

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Суперпользователь должен иметь is_superuser=True.')

        return self._create_user(username, email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    """
    Определяет наш пользовательский класс User.
    Требуется имя пользователя, адрес электронной почты и пароль.
    """
    id = models.AutoField(primary_key=True, null=False)
    username = models.TextField(db_index=True, max_length=255, unique=True, blank=False, verbose_name='Имя пользователя')
    email = models.EmailField(
        validators=[validators.validate_email],
        unique=True,
        verbose_name='Почта'
    )
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)

    lastName = models.TextField(max_length=30, verbose_name='Фамилия')
    firstName = models.TextField(max_length=30, verbose_name='Имя')
    middleName = models.TextField(max_length=30, verbose_name='Отчество', null=True, blank=True)
    role = models.ForeignKey(Role, related_name='+', on_delete=models.CASCADE, verbose_name='Роль', null=True, blank=True)
    studyGroup = models.ForeignKey(StudyGroup, related_name='+', on_delete=models.CASCADE, verbose_name='Группа', null=True, blank=True)
    phone = models.TextField(max_length=30, verbose_name='Телефон')
    workPlace = models.TextField(max_length=200, verbose_name='Место работы', null=True, blank=True)
    position = models.TextField(max_length=100, verbose_name='Должность', null=True, blank=True)
    academicRank = models.TextField(max_length=30, verbose_name='Ученое звание', null=True, blank=True)
    academicDegree = models.TextField(max_length=50, verbose_name='Ученая степень', null=True, blank=True)
    emailConfirmed = models.BooleanField(default=False, verbose_name='Почта подтверждена')
    passwordResetToken = models.TextField(max_length=40, verbose_name='Токен сброса пароля', null=True, blank=True)
    emailConfirmToken = models.TextField(max_length=30, verbose_name='Токен подтверждения почты', null=True, blank=True)
    last_login = models.DateTimeField(verbose_name='Последний вход', blank=True, null=True)
    # Свойство `USERNAME_FIELD` сообщает нам, какое поле мы будем использовать для входа.
    USERNAME_FIELD = 'username'

    REQUIRED_FIELDS = ('email',)

    # Сообщает Django, что класс UserManager, определенный выше,
    # должен управлять объектами этого типа.
    objects = UserManager()

    class Meta:
        verbose_name_plural = 'Пользователь'
        verbose_name = 'Пользователи'

    def __str__(self):
        """
        Возвращает строковое представление этого `User`.
        Эта строка используется, когда в консоли выводится `User`.
        """
        return self.lastName + " " + self.firstName

    @property
    def token(self):
        """
        Позволяет нам получить токен пользователя, вызвав `user.token` вместо
        `user.generate_jwt_token().

        Декоратор `@property` выше делает это возможным.
        `token` называется «динамическим свойством ».
        """
        return self._generate_jwt_token()

    def get_full_name(self):
        """
        Этот метод требуется Django для таких вещей,
        как обработка электронной почты.
        Обычно это имя и фамилия пользователя.
        Поскольку мы не храним настоящее имя пользователя,
        мы возвращаем его имя пользователя.
        """
        return self.username

    def get_short_name(self):
        """
        Этот метод требуется Django для таких вещей,
        как обработка электронной почты.
        Как правило, это будет имя пользователя.
        Поскольку мы не храним настоящее имя пользователя,
        мы возвращаем его имя пользователя.
        """
        return self.username

    def _generate_jwt_token(self):
        """
        Создает веб-токен JSON, в котором хранится идентификатор
        этого пользователя и срок его действия
        составляет 60 дней в будущем.
        """
        dt = datetime.now() + timedelta(minutes=180)

        token = jwt.encode({
            'id': self.pk,
            'exp': int(dt.strftime('%s'))
        }, settings.SECRET_KEY, algorithm='HS256')

        return token.decode('utf-8')
