import jwt
import logging

from django.views import View
from django.http import JsonResponse, Http404
from django.shortcuts import render, redirect

from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.hashers import make_password
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import authenticate, login, logout
from django.contrib.sites.shortcuts import get_current_site


from .models import User, Token, TokenType
from .utils import *

from apps.subscriptions.models import *

# Create a logger for this file
logger = logging.getLogger(__file__)

# Create your views here.

def is_ajax(request):
    return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'

class LoginView(View):
    def get(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('landing:index')
        template = 'accounts/signin.html'
        context={}
        return render(request, template, context)

    def post(self, request, *args, **kwargs):
        # if not request.user.is_authenticated:
        email       = request.POST.get('email')
        password    = request.POST.get('password')
        remember_me = request.POST.get('remember_me')

        # Check
        if email and password:
            user = User.objects.filter(email=email).first()
            # Check
            if user:
                
                # Check If user is not verified.
                if not user.is_verified and not user.is_superuser :
                    # Create an informational message and send a mail to user .
                    return JsonResponse({'result': 'info', 'message': f"Sorry, your account Not Verified, Check Your Email {email} For Verification Link Or Contact Support"})
                    #! send an email to the user to activate his account.
                # Check If user is not active.
                if not user.is_active and not user.is_superuser :
                    # Create an informational message and send a mail to user .
                    return JsonResponse({'result': 'info', 'message': "Sorry, your account is in-Active, Check Your Email For Activation Request Link Or Contact Support"})
                    #! send an email to the user to activate his account.
                
                # Check If password is Correct.
                if not user.check_password(password):
                    return JsonResponse({'result': 'error', 'message': "Password is incorrect. Kindly Try Again With A Valid Password"})
                # authenticate the user
                user = authenticate(request, email=email, password=password)
                if user is not None:
                    # Check if user want to be remembered by browser.
                    if not remember_me:
                        # set session expiry to 0 seconds. So it will automatically close the session after the browser is closed.
                        request.session.set_expiry(0)
                        # Set session as modified to force data updates/cookie to be saved.
                        request.session.modified = True
                    # login the user
                    login(request, user)
                    # else browser session will be as long as the session cookie time "SESSION_COOKIE_AGE" defined in settings.py
                    return JsonResponse({'result': 'success', 'message': "You are logged in."})
            else:
               return JsonResponse({'result': 'error', 'message': "Email does not exist. Kindly Try Again With A Valid Email."})
        return JsonResponse({'result': 'error', 'message': "go to 404"})
            
class LogoutView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        logout(request)
        self.request.session.flush()
        return redirect('accounts:signin')

class RegistrationView(View):
    def get(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('landing:index')

        plan_obj = None
        message = None

        try:
            plan_id = request.session['plan']
            plan_obj = Plan.objects.get(plan_id__iexact=plan_id)
        except KeyError or Plan.DoesNotExist:
            # request.session['plan'] = "P-2E356653NJ554300BML54IQY"
            # plan_obj = Plan.objects.get(plan_id='P-2E356653NJ554300BML54IQY')
            # message = "We are setting basic plan by default. If you wanna change it Select another plan."
            return redirect('landing:index')

        context = {
            "plan_obj": plan_obj,
            "message": message
        }
        template = 'accounts/signup.html'
        return render(request, template, context)

    def post(self, request, *args, **kwargs):
        # code to process a POST request
        # if is_ajax(request):
        # if not request.user.is_authenticated:
        response = None
        if "membership" in request.POST:
            # add plan in session
            if request.POST.get('membership') not in request.session:
                request.session['plan'] = request.POST.get('membership')
                try:
                    if Plan.objects.filter(plan_id__iexact=request.session['plan']).exists():
                        response={
                            'result':"Found Success",
                            'message': "Membership ID Found Success"
                        }
                        logger.info(f"ID {request.POST['membership']}")
                        return JsonResponse(response)
                except Plan.DoesNotExist:
                    response = {"result": "Not Found","message": "Please Select Plan Correctly"}
                    return JsonResponse(response)

        # save payment
        if User.objects.filter(email=request.POST.get("email")).first():
            response = {
                "result": "error",
                "message": "Email already used. Kindly Try Again With Another Email.",
            }
            return JsonResponse(response)

        # Create a new user.
        new_user = User()
        new_user.first_name = request.POST.get("first_name")
        new_user.last_name  = request.POST.get("last_name")
        new_user.username   = f"{new_user.first_name} {new_user.last_name}"
        new_user.email      = request.POST.get("email")
        new_user.password   = make_password(request.POST.get("password"))
        new_user.is_active  = False
        new_user.save()

        # Send an email to the user to activate his account.
        send_activation_email(new_user, get_current_site(request))

        # create a subscription object linked to new user
        plan_instance = Plan.objects.get(plan_id=request.POST['PlanID'])
        new_subscription = Subscription()
        new_subscription.plan = plan_instance
        new_subscription.user = new_user
        new_subscription.is_active = False # update it by webhook
        new_subscription.paid_status = False # updated it by webhook
        new_subscription.save()

        # check payment status
        new_payment = Payment()
        if 'OrderID' in request.POST:
            new_payment.transactionID = request.POST.get('OrderID')
            new_payment.conversationID = request.POST.get('ConversationID')
            new_payment.subscription = new_subscription
            new_payment.sub_id = request.POST.get('SubscriptionID')
            new_payment.is_active = True
        new_payment.save()

        response = {
                "result": "success",
                "message": f"You are signed up. Check your email {new_user.email} to activate your account.",
            }
        return JsonResponse(response)

class PasswordResetView(View):
    def get(self, request, *args, **kwargs):
        template = 'accounts/reset_password.html'
        context={'message': ""}
        return render(request, template, context)

    def post(self, request, *args, **kwargs):
        # code to process a POST request
        if is_ajax(request):
            if not request.user.is_authenticated:
                user = User.objects.filter(email=request.POST.get('email')).first()
                if user:
                    send_password_reset_email(user, get_current_site(request))
                    response = {'result': 'success', 'message': f"Check your email {user.email} to Reset Your Password."}
                else:
                    response = {'result': 'error', 'message': f"Email does not exist. Kindly Try Again With A Valid Email."}
                return JsonResponse(response)

class PasswordChangeView(View):
    def get(self, request, token, *args, **kwargs):
        validate_obj, token_obj = self.validate_token(token)
        if not token_obj:
            messages.error(request, validate_obj["message"])
            if 'expired' in validate_obj and validate_obj["expired"]:
                return redirect('landing:password_reset')
            raise Http404(validate_obj["message"])
            # return redirect('landing:index')
        template = 'accounts/password_change.html'
        context = {'message': ""}
        return render(request, template, context)

    def post(self, request, token, *args, **kwargs):
        # code to process a POST request
        if is_ajax(request):
            validate_obj, token_obj = self.validate_token(token)
            if not validate_obj["status"]:
                messages.error(request, validate_obj["message"])
                if 'expired' in validate_obj and validate_obj["expired"]:
                    return redirect('landing:password_reset')
                raise Http404(validate_obj["message"])
                # return redirect('landing:index')
            token_obj.user.password = make_password(request.POST.get('password'))
            token_obj.user.save() # Save User With New Password
            token_obj.delete() # Delete the token from the database
            response = {'result': 'success', 'message': f"Your password has been changed."}
            return JsonResponse(response)
    
    def validate_token(self, token):
        """
        Validate the given token and return a dictionary with a message, status and the token object if valid.
        :param token: The token to be validated
        :return: A dictionary containing the validation message, status and the token object if valid.
        """
        try:
            # Get the token object from the database
            token_obj = Token.objects.get(token=token)
            # Check if the token type is valid
            if token_obj.token_type != TokenType.PasswordReset:
                return {'message': 'Invalid Token', 'status': False}, None
            # Check if the token has expired
            if token_obj.expires_at < timezone.now():
                return {'message': 'Token has expired', 'status': False}, None
            # Decode the token
            decoded_token = jwt.decode(token, token_obj.secret_key, algorithms=['HS256'])
            # check if the email in token match the user email
            if decoded_token.get('user_id') != token_obj.user.email:
                return {'message': 'Invalid Token', 'status': False}, None
            # check the token type
            if decoded_token.get('token_type') != TokenType.PasswordReset:
                return {'message': 'Invalid Token', 'status': False}, None
                
            return {'message': 'Valid Token', 'status': True}, token_obj
        
        except jwt.ExpiredSignatureError:
            return {'message': 'Token has expired', 'status': False, 'expired': False }, None
        except jwt.DecodeError:
            return {'message': 'Invalid Token', 'status': False}, None
        except Token.DoesNotExist:
            return {'message': 'Invalid Token', 'status': False}, None

class UpdateEmailView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        if is_ajax(request):
            if request.user.is_authenticated:
                new_email = request.POST.get('new_email')
                password = request.POST.get('password')
                if new_email and password:
                    if request.user.check_password(password):
                        if email_change_limit_reached(request.user):
                            return JsonResponse({'status':'error', 'message':'Email change limit reached'})
                        if User.objects.filter(email=new_email).exclude(pk=request.user.pk).exists():
                            return JsonResponse({'status':'error', 'message':'Email already in use'})
                        if request.user.email == new_email :
                            return JsonResponse({'status':'error', 'message':'This Email is your current Account Email'})
                        request.user.update_email(new_email)
                        # update_session_auth_hash(request, request.user)
                        return JsonResponse({'status':'success', 'message':'Email updated successfully'})
                    else:
                        return JsonResponse({'status':'error', 'message':'Incorrect password'})
                else:
                    return JsonResponse({'status':'error', 'message':'New email Or password not provided'})
            # go to 404
        # go to 404

class UpdatePasswordView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        if is_ajax(request):
            if request.user.is_authenticated:
                new_password = request.POST.get('new_password')
                password = request.POST.get('password')
                if new_password and password:
                    if request.user.check_password(password):
                        if password_change_limit_reached(request.user):
                            return JsonResponse({'status':'error', 'message':'Password change limit reached'})
                        request.user.update_password(new_password)
                        update_session_auth_hash(request, request.user)
                        return JsonResponse({'status': 'success', 'message': 'Password updated successfully'})
                    else:
                        return JsonResponse({'status':'error', 'message':'Incorrect password'})
                else:
                    return JsonResponse({'status': 'error', 'message': 'New password or Old password not provided'})
            # go to 404
        # go to 404


def activate(request, token):
    """
        Verify the user's account using the provided token.
        :param request: The request object
        :param token: The verification token
        :return: A redirect to the home page
    """
    try:
        # Get the token object from the database
        token_obj = Token.objects.get(token=token)
        # Check if the token type is valid
        if token_obj.token_type != TokenType.EmailVerification:
            raise ValueError('Invalid Token')
        # Check if the token has expired
        if token_obj.expires_at < timezone.now():
            raise ValueError('Token has expired')
        # Decode the token
        decoded_token = jwt.decode(token, token_obj.secret_key, algorithms=['HS256'])
        # check if the email in token match the user email
        if decoded_token.get('user_id') != token_obj.user.email:
            raise ValueError('Invalid Token')
        # check the token type
        if decoded_token.get('token_type') != TokenType.EmailVerification:
            raise ValueError('Invalid Token')    

        user = token_obj.user
        if user.is_active:
            messages.info(request, 'Your account is already active.')
            return redirect('home')
        else:
            user.is_active = True
            user.is_verified = True
            user.save()
            token_obj.delete()
            messages.success(request, 'Your account has been verified.')
            return redirect('home')

    except jwt.ExpiredSignatureError:
        # send_activation_email(new_user, get_current_site(request))
        messages.error(request, 'Token has expired')
        return redirect('home')
    except jwt.DecodeError:
        # send_activation_email(new_user, get_current_site(request))
        messages.error(request, 'Invalid Token')
        return redirect('home')
    except Token.DoesNotExist:
        messages.error(request, 'Invalid Token')
        return redirect('home')
    except Exception as e:
        messages.error(request, f'An error occurred: {e}')
        return redirect('home')

def deactivate_account(request):
    """
    Disable the user's account
    :param request: The request object
    :return: A redirect to the home page
    """
    if is_ajax(request) and request.method == 'POST':
        user = request.user
        user.is_active = False
        user.save()
        # send :: Account Re-Activation Email
        logout(request)
        request.session.flush()
        return JsonResponse({'message': 'Your account has been deactivated.', 'status': 'success'})

