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 django.conf import settings
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from .models import User, Token, TokenType, SocialAuth
from .utils import *


from apps.subscriptions.models import *



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

plans = settings.MAILER_LITE_GROUPS
# Create your views here.

# check ajax request
def is_ajax(request):
    return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'

class LoginSocialUser(View):
    def post(self,request,*args,**kwargs):
        email = request.POST.get("email")
        # save social user if user signup using google
        if "provider" in request.POST:
            user = User.objects.filter(email=email).first()
            if user:
                social_user = SocialAuth()
                social_user.user = user
                social_user.provider = request.POST['provider']
                # set custom model backend for signin user
                user.backend = "apps.accounts.backends.NonePasswordBackend"
                social_user.save()
                
                new_user = authenticate(email=user.email, password=None)

                if new_user is not None:
                    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 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"})


@method_decorator(login_required(login_url="/auth/signin"), name="dispatch")
class LogoutView(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.get('plan',"P-22742094HB582092PMWHOFHI")
          plan_obj = Plan.objects.get(plan_id__iexact=plan_id)
      except KeyError or Plan.DoesNotExist:
          # request.session['plan'] = "P-4BF67411MU090401AMPDZABY"
          # 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:
                # here we should get one plan by objects.get() ?
                # in this part we are checking existance of plan in database if we used get or filter (are the same)
                  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  = True
      new_user.save()

      # Send an email to the user to activate his account.
      # send_activation_email(new_user, get_current_site(request))
      activation_url = f"https://{ get_current_site(request) }/auth/activate/{ new_user.token('email_verification', duration = 60*60*6) }" 

      plan_instance = Plan.objects.get(plan_id=request.session['plan'])

      add_mailer_user(
          email=new_user.email,
          first_name=new_user.first_name,
          fields={
                  "last_name":new_user.last_name,
                  'email_activation_link':activation_url,
                  },
          # i maked change in this line 
          # nice hada blan
          # should we send mail here or when subscription are created ?
          # we will send mail when the user created
          group=f"{plan_instance.plan_name}_{plan_instance.plan_duration}" if (plan_instance and plan_instance.plan_name != 'Free') else "Free_Plan",
      )

      response = {
              "result": "success",
              "message": f"You are signed up. Check your email {new_user.email} to activate your account.",
          }
      return JsonResponse(response)

class RegisterSocialUser(View):
    def post(self,request):
        print("Receive From Social")
        # save social user if user signup using google
        if "provider" in request.POST:
            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.is_active  = True
            new_user.save()

            # Send an email to the user to activate his account.
            # send_activation_email(new_user, get_current_site(request))
            activation_url = f"https://{ get_current_site(request) }/auth/activate/{ new_user.token('email_verification', duration = 60*60*4) }" 

            social_user = SocialAuth()
            social_user.user = new_user
            social_user.pic_url = request.POST['picture']
            social_user.provider = request.POST['provider']
            social_user.save()
            # authenticate the user
            user = authenticate(request,email=new_user.email, password=None)
            
            if user is not None:
                # login the user
                
                login(request, user)
                print("USER ARE AUTHENTICATED SUCCESS! ")
                response = {'result': 'success', 'message': "You are logged in."}
                
                # create a subscription object linked to new user
                plan_instance = Plan.objects.get(plan_id=request.POST.get('plan_id'))
                new_subscription = Subscription()
                new_subscription.plan = plan_instance
                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()
                # add user in mailer list group
  
                # check payment status
                if 'OrderID' in request.POST:
                    new_payment = Payment()
                    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()
                    
            
                    add_mailer_user(
                        email=new_user.email,
                        first_name=new_user.first_name,
                        group=f'{plan_instance.plan_name}_{plan_instance.plan_duration}',
                        fields={
                            "last_name":new_user.last_name,
                            "email_activation_link":activation_url
                        }
                        
                    )
                else:
                    # handle free plan user 
                    add_mailer_user(
                        email=new_user.email,
                        first_name=new_user.first_name,
                        group="Free_Plan",
                        fields={
                            "last_name":new_user.last_name,
                            "email_activation_link":activation_url
                        }
                    )
                    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:
                    # initialize user to mailerlite group
                    add_mailer_user(
                        email=user.email,
                        first_name=user.first_name,
                        group='Forgot_Pass',
                        fields={
                            "last_name":user.last_name,
                            "reset_password_link": f"https://{get_current_site(request).domain}/auth/password_change/{user.token('password_reset')}",
                        }
                        )
                    
                    # 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 and user.is_verified:
            messages.info(request, 'Your account is already active.')
            return redirect('accounts:signin')
        else:
            user.is_active = True
            user.is_verified = True
            user.save()
            token_obj.delete()
            add_mailer_user(
                email=user.email,
                first_name=user.first_name,
                group="Welcome_Group",
                fields={
                    "last_name":user.last_name
                }
                )
            messages.success(request, 'Your account has been verified.')
            return redirect('accounts:signin')

    except jwt.ExpiredSignatureError:
        # send_activation_email(new_user, get_current_site(request))
        messages.error(request, 'Token has expired')
        return redirect('accounts:signin')
    except jwt.DecodeError:
        # send_activation_email(new_user, get_current_site(request))
        messages.error(request, 'Invalid Token')
        return redirect('accounts:signin')
    except Token.DoesNotExist:
        messages.error(request, 'Invalid Token')
        return redirect('accounts:signin')
    except Exception as e:
        messages.error(request, f'An error occurred: {e}')
        return redirect('accounts:signin')

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'})


class CheckEmail(View):
    def get(self,request,email):
        return JsonResponse(
            {"are_exists":User.objects.filter(email=email).exists()}
            )
