from django.urls import reverse_lazy
from django.views import View
from django.views.generic import FormView
from django.views.generic.edit import UpdateView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.contrib.auth import login,authenticate,logout
from django.contrib import messages
from django.shortcuts import redirect, render
from django.db import IntegrityError
import hashlib
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.decorators import login_required

from .forms import (UserSignUpForm, 
                    SigninForm,
                    CustomPasswordResetForm,
                    ProfilePictureUpdateForm,
                    UpdateProfileForm)
from django.contrib.auth.views import (PasswordResetView,
                                       PasswordChangeView)

from django.http import JsonResponse
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.mixins import UserPassesTestMixin

from .models import CustomUser

from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
from .tokens import account_activation_token
from django.contrib import messages
from django.contrib.auth import authenticate, login
from django.utils.http import url_has_allowed_host_and_scheme


from .utils import (send_activation_email,
                    redirect_to_next_or_home,
                    )
from at_django_boilerplate.utils.encryption_utils import EncryptionUtils

from django.shortcuts import render, redirect
from .forms import UserSignUpForm



import logging
logger = logging.getLogger(__name__)

class CheckUsernameAvailability(View):
    def get(self, request):
        username = request.GET.get('username', None)
        is_taken = CustomUser.objects.filter_by_email(username).exists()
        data = {
            'is_taken': is_taken
        }
        return JsonResponse(data)


class SignupView(FormView):
    template_name = 'registration/signup.html'
    form_class = UserSignUpForm
    success_url = reverse_lazy('home')

    def post(self, request, *args, **kwargs):
        next_url = request.GET.get('next')
        user_form = self.form_class(request.POST)

        if user_form.is_valid():
            email = user_form.cleaned_data['email'].lower()
            contact_number = user_form.cleaned_data.get('contact_number')

            email_exists = CustomUser.objects.filter_by_email(email).exists()
            number_exists = False

            if contact_number:
                number_exists = CustomUser.objects.filter_by_contact_number(contact_number).exists()

            if email_exists:
                user_form.add_error('email', 'Email is already in use.')
            elif number_exists:
                user_form.add_error('contact_number', 'Contact number is already in use.')
            else:
                try:
                    user = user_form.save(commit=False)
                    user.set_password(user.password)
                    user.save()
                    login(request, user)

                    user.is_active = True
                    user.save()

                    send_activation_email(
                        request=request,
                        user=user,
                        to_email=user.get_decrypted_email()
                    )

                    # return redirect(next_url or 'verify-email-done')
                    return redirect('dashboard')

                except IntegrityError:
                    # Rollback if needed
                    user.delete()

        return self.render_to_response(self.get_context_data(form=user_form))


def verify_email(request):
    return render(request, 'registration/verify_email.html')


def verify_email_done(request):
    return render(request, 'registration/verify_email_done.html')


def verify_email_confirm(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = CustomUser.objects.get(pk=uid)
    except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
        user = None
    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()
        messages.success(request, 'Your email has been verified.')
        return redirect('verify-email-complete')   
    else:
        messages.warning(request, 'The link is invalid.')
    return render(request, 'registration/verify_email_confirm.html')


def verify_email_complete(request):
    return render(request, 'registration/verify_email_complete.html')



class LoginPageView(View):
    template_name = 'login.html'
    form_class = SigninForm

    def get(self, request):
        if request.user.is_authenticated:
            next_url = request.GET.get('next')
            if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}):
                return redirect(next_url)
            return redirect('home')

        form = self.form_class()
        return render(request, self.template_name, {'form': form, 'message': ''})

    def post(self, request):
        print('Login')
        form = self.form_class(request.POST)
        message = 'Login failed!'

        if form.is_valid():
            print('Form Valid')
            username = form.cleaned_data['username'].lower()
            password = form.cleaned_data['password']

            user = authenticate(request, username=username, password=password)
            # user.backend = 'accounts.auth_backends.CustomAuthBackend'  # <--- key line added
            print(f"Authenticated User: {user}")
            if user:
                if user.is_active:
                    login(request, user)
                    return redirect_to_next_or_home(request)
                else:
                    messages.error(request, 'Please verify your account before logging in.')
            else:
                messages.error(request, 'Invalid username or password.')
        else:
            messages.error(request, 'Invalid input.')

        return render(request, self.template_name, {
            'form': form,
            'message': message,
        })



class LogoutView(View):
    def get(self, request):
        logout(request)
        return redirect('home')


class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    template_name = 'reset/password_reset.html'
    email_template_name = 'reset/password_reset_email.html'
    html_email_template_name = 'reset/password_reset_email_html.html'
    subject_template_name = 'reset/password_reset_subject.txt'

    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."
    success_url = reverse_lazy('password_reset_done')
    token_generator = default_token_generator
    form_class = CustomPasswordResetForm

class ResetPasswordDoneView(View):
    template_name = 'reset/password_reset_done.html'
    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."

    def get(self, request):
        return render(request=request,template_name=self.template_name)



class ChangePasswordView(SuccessMessageMixin, PasswordChangeView):
    template_name = 'change_password.html'
    success_message = "Successfully Changed Your Password"
    success_url = reverse_lazy('home')




class CustomUserListView(ListView):
    model = CustomUser
    template_name = 'user_list.html'
    users = 'CustomUsers'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        users = CustomUser.objects.all()
        context['users'] = [{
            'username': custom_user.username,
            'name': custom_user.get_decrypted_name(),
            'email': custom_user.get_decrypted_email(),
            'birthday': custom_user.birthday,
            'address': custom_user.get_decrypted_address(),
            'contact_number': custom_user.get_decrypted_contact_number()
        } for custom_user in users]
        return context

class ProfileView(DetailView):
    model = CustomUser
    template_name = 'profile/profile.html'
    context_object_name = 'custom_user'

    def get_object(self):
        return self.request.user

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Initialize the form with the current user's data
        form = ProfilePictureUpdateForm(instance=self.get_object())
        context['update_profile_photo_form'] = form
        return context

class UpdateProfileView(UserPassesTestMixin, UpdateView):
    model = CustomUser
    form_class = UpdateProfileForm
    template_name = 'profile/update_profile.html'

    def test_func(self):
        obj = self.get_object()
        requested_by_user = self.request.user
        return (obj == requested_by_user)

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        # Decrypt the data before passing it to the form
        encryption_tool = EncryptionUtils()
        contact_number = None
        if self.object.contact_number:
            contact_number =  encryption_tool.decrypt(self.object.contact_number)
        
        initial_data = {
            'contact_number': contact_number
        }
        form = self.form_class(instance=self.object, initial=initial_data)
        return self.render_to_response(self.get_context_data(form=form))

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.form_class(request.POST, instance=self.object)
        if form.is_valid():
            obj = form.save(commit=False)
            encryption_tool = EncryptionUtils()
            # address = form.cleaned_data['address']
            # obj.address = encryption_tool.encrypt(address) 
            contact_number = form.cleaned_data['contact_number']
            if all([contact_number!=None,contact_number!='']):
                obj.contact_number = encryption_tool.encrypt(contact_number) 
            obj.save()

            return self.form_valid(form)
        else:
            # Decrypt the data before re-rendering the form with errors
            encryption_tool = EncryptionUtils()
            contact_number = None
            if self.object.contact_number:
                contact_number =  encryption_tool.decrypt(self.object.contact_number)
            
            initial_data = {
                'contact_number': contact_number
            }
            form = self.form_class(instance=self.object, initial=initial_data)

            return self.form_invalid(form)

@login_required
def update_profile_picture(request):
    if request.user.pk !=None:
        user_profile = request.user
        if request.method == 'POST':
            form = ProfilePictureUpdateForm(request.POST, request.FILES, instance=user_profile)
            if form.is_valid():
                form.save()
                messages.success(request, 'Your profile picture has been updated successfully!')
                return redirect('user_profile')
        else:
            form = ProfilePictureUpdateForm(instance=user_profile)
    return render(request, 'profile/update_profile_picture.html', {'form': form})


from django.core.validators import EmailValidator, ValidationError
from django.core.cache import cache
from .utils import send_otp, verify_otp
from .models import CustomUser
import re

PHONE_REGEX = re.compile(r'^\+?\d{10,15}$')



def is_email(identifier):
    validator = EmailValidator()
    try:
        validator(identifier)
        return True
    except ValidationError:
        return False

def request_otp_view(request):
    if request.method == 'POST':
        identifier = request.POST.get('identifier', '').strip().lower()
        
        if not identifier:
            messages.error(request, "Please enter your email or mobile number.")
            return redirect('login')  # Assuming OTP modal is on the login page
        print(f"Identifier: {identifier}")
        user = None
        if is_email(identifier):
            print(f"Email: {identifier}")
            user = CustomUser.objects.filter_by_email(email=identifier).first()
        else:
            if not PHONE_REGEX.match(identifier):
                messages.error(request, "Invalid mobile number format.")
                return redirect('login')
            print(f"Mobile hash: {identifier}")
            user = CustomUser.objects.filter_by_contact_number(contact_number=identifier).first()
        print(f"User: {user}")
        if not user:
            messages.error(request, "No user found with this email or mobile number.")
            return redirect('login')

        # Rate limiting
        cache_key = f"otp_rate_limit_{identifier}"
        if cache.get(cache_key):
            messages.error(request, "Please wait before requesting another OTP.")
            return redirect('login')

        # Send OTP
        success, _ = send_otp(identifier, purpose='login')
        if success:
            cache.set(cache_key, True, timeout=60)  # 60 seconds
            request.session['otp_identifier'] = identifier
            messages.success(request, "OTP sent successfully.")
            
            return redirect('verify_otp')
        else:
            messages.error(request, "Failed to send OTP. Please try again.")
            return redirect('login')

    return redirect('login')


def verify_otp_view(request):
    identifier = request.session.get('otp_identifier')
    if not identifier:
        messages.error(request, "Session expired. Please request a new OTP.")
        return redirect('login')

    if request.method == 'POST':
        user_otp = request.POST.get('otp', '').strip()

        if not user_otp:
            messages.error(request, "Please enter the OTP.")
            return render(request, 'registration/verify_otp.html')

        if verify_otp(identifier, user_otp):
            # Match user
            if is_email(identifier):
                print(f"Email: {identifier}")
                user = CustomUser.objects.get_by_email(email=identifier)
            else:
                
                user = CustomUser.objects.get_by_contact_number(contact_number=identifier)

            # user.backend = 'django.contrib.auth.backends.ModelBackend'  # <--- key line added
            login(request, user)
            messages.success(request, "Logged in successfully.")
            request.session.pop('otp_identifier', None)
            return redirect('home')
        else:
            messages.error(request, "Invalid or expired OTP.")

    return render(request, 'registration/verify_otp.html')
