import jwt
import secrets
import logging

from django.db import models

from django.utils import timezone
from django.core.mail import send_mail
from django.core.exceptions import ValidationError

from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _
import uuid
from .managers import UserManager

# Create a logger for this file
logger = logging.getLogger(__file__)

# Create your models here.

class User(AbstractUser):
    """ Custom User Model """

    username      = models.CharField(_('UserName'), max_length=255, unique=False, blank=True, null=True)
    email         = models.EmailField(_('Email'), unique=True, blank=False, error_messages={'unique': _("A user with that email address already exists."), },)
    is_verified   = models.BooleanField(_('Verified Account'), default=False)

    # EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

    objects = UserManager()

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

    def get_full_name(self):
        return f"{self.first_name} {self.last_name}"

    def get_short_name(self):
        return self.first_name

    def check_password(self, raw_password):
        from django.contrib.auth.hashers import check_password
        return check_password(raw_password, self.password)

    @property
    def token(self, token_type):
        """  Allows us to get a user's token by calling `user.token` """
        
    # @property
    # def token(self):
    #     """
    #     Allows us to get a user's token by calling `request.user.token`
    #     """
    #     def generateToken(token_type):
    #         response, token = Token.create_token(self, token_type, settings.TOKEN_EXPIRY_SECONDS)
    #         return token
    #     return generateToken
    
    @property
    def token(self, ):
        """
        Allows us to get a user's token by calling `request.user.token`
        """
        def generateToken(token_type, duration=None): 
            from django.conf import settings
            if duration == None: duration = settings.TOKEN_EXPIRY_SECONDS
            response, token = Token.create_token(self, token_type, duration)
            return token
        return generateToken

    def update_email(self, new_email):
        EmailHistory.objects.create(
            user=self,
            old_email=self.email,
            new_email=new_email
        )
        self.email = new_email
        self.save()

    def update_password(self, new_password):
        PasswordHistory.objects.create(
            user=self,
            old_password=self.password,
            new_password=new_password
        )
        self.set_password(new_password)
        self.save()

    def email_user(self, subject, message, from_email=None, **kwargs):
        """ Sends an email to this User. """
        send_mail(subject, message, from_email, [self.email], **kwargs)


class LoginInUser(models.Model):
    user        = models.OneToOneField(User,  null=False, related_name='login_in_user', on_delete=models.CASCADE)
    session_key = models.CharField(_("Session key"), null=True, blank=True, max_length=54)
    ip_address  = models.GenericIPAddressField(_("IP Address"), null=True, protocol="both", unpack_ipv4=False)

    def __str__(self):
        return f"{self.user.email}"


class TokenType(models.TextChoices):
    EmailVerification  = 'email_verification', 'Email Verification'
    PasswordReset      = 'password_reset', 'Password Reset'
    OtherType          = 'other_type', 'Other Type'


class Token(models.Model):
    user       = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens')
    token      = models.CharField(max_length=256, unique=True)
    secret_key = models.CharField(max_length=256, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()
    token_type = models.CharField(max_length=100, choices=TokenType.choices)

    def __str__(self):
        return f"Token With Type {self.token_type} For User {self.user.email}"

    @classmethod
    def create_token(cls, user, token_type, expires_in=3600):
        from datetime import timedelta
        expires_at = timezone.now() + timedelta(seconds=expires_in)
        secret_key = secrets.token_hex(32)

        # Delete existing token for the user if it exists
        existing_token = cls.objects.filter(user=user, token_type=token_type).first()
        if existing_token:
            existing_token.delete()

        payload = {'user_id': user.email, 'exp': expires_at, 'token_type':token_type}
        token = jwt.encode(payload, secret_key, algorithm='HS256')
        cls.objects.create(user=user, token=str(token), expires_at=expires_at,token_type=token_type, secret_key=secret_key)
        return {'message': 'Token created successfully', 'status': 'success'}, token


class EmailHistory(models.Model):
    user       = models.ForeignKey(User, on_delete=models.CASCADE)
    old_email  = models.EmailField()
    new_email  = models.EmailField()
    created_at = models.DateTimeField(default=timezone.now, editable=False)

    def __str__(self):
        return f'{self.user} - {self.created_at}'


class PasswordHistory(models.Model):
    user         = models.ForeignKey(User, on_delete=models.CASCADE)
    old_password = models.CharField(max_length=255)
    new_password = models.CharField(max_length=255)
    created_at   = models.DateTimeField(default=timezone.now, editable=False)

    def __str__(self):
        return f'{self.user} - {self.created_at}'


#
class Provider(models.TextChoices):
    GOOGLE = "Google", "google"
    APPLE = "Apple", "apple"


class SocialAuth(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    
    provider = models.CharField(
        verbose_name=_("provider"),
        max_length=30,
        choices=Provider.choices,
        default=Provider.GOOGLE,

    )

    last_login  = models.DateTimeField(verbose_name=_("last login"), auto_now=True)
    date_joined = models.DateTimeField(verbose_name=_("date joined"), auto_now_add=True)
    pic_url     = models.CharField(max_length=255)

    class Meta:
        verbose_name = _("Social Auth")
        verbose_name_plural = _("Social Auths")

    def __str__(self):
        return self.user.email
