import logging
from datetime import datetime

from django.shortcuts import render, redirect
from django.views.generic import FormView
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth import authenticate, login, logout, update_session_auth_hash, get_user_model
from django.contrib.auth.tokens import default_token_generator
from django.contrib import messages
from django.template import loader
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.db.models.query_utils import Q

from brdwt.settings import EMAIL_HOST_USER
from utils.crypto import AesCrypto
from utils.enums import LOGIN_REDIRECT, UserTypeEnum
from utils.validations import validate_password, validate_role
from users.forms import CaptchaForm, PasswordResetRequestForm, SetPasswordForm
from users.models import Users

logger = logging.getLogger(__name__)
User = get_user_model()


def Login(request):
    page_title = "BRDWT | Login"
    template = "users/login.html"
    CaptchaForms = CaptchaForm()
    context = {'page_title': page_title, 'CaptchaForm': CaptchaForms}
    if request.method == "POST" and "submitForm" in request.POST:
        CaptchaForms = CaptchaForm(request.POST)
        if CaptchaForms.is_valid():
            username = request.POST['newUsername']
            password = request.POST['newPassword']
            usernameLength = int(request.POST['ul'])
            passwordLength = int(request.POST['pl'])
            key = request.POST['key']
            iv = request.POST['iv']
            aes = AesCrypto(key, iv)
            decryptedUsername = aes.decrypt(username)
            decryptedPassword = aes.decrypt(password)
            decryptedUsername = decryptedUsername[:usernameLength]
            decryptedPassword = decryptedPassword[:passwordLength]
            if user := authenticate(request, username=decryptedUsername, password=decryptedPassword):
                if user.user_type == UserTypeEnum.ADMIN.value or user.user_type == UserTypeEnum.OPERATOR.value:
                    login(request, user)
                    return redirect('/dashboard')
                else:
                    login(request, user)
                current_year = datetime.now().year
                if datetime.now().month <= 3:
                    previous_fin_year = f"{current_year - 2}-{current_year - 1}"
                    current_fin_year = f"{current_year - 1}-{current_year}"
                    next_fin_year = f"{current_year}-{current_year + 1}"
                else:
                    previous_fin_year = f"{current_year - 1}-{current_year}"
                    current_fin_year = f"{current_year}-{current_year + 1}"
                    next_fin_year = f"{current_year + 1}-{current_year + 2}"
                request.session['previous_fin_year'] = previous_fin_year
                request.session['current_fin_year'] = current_fin_year
                request.session['next_fin_year'] = next_fin_year

                request.session['username'] = username
                if destination := LOGIN_REDIRECT.get(user.user_type):
                    return redirect(destination)

            else:
                msg = 'Invalid username or password!'
                logger.error(msg)
                context['errormessage'] = msg
        else:
            context['errorCap'] = CaptchaForms.errors
    return render(request, template, context)


class UserManagement:

    def __init__(self, user_type):
        self.user_type = user_type

    @validate_role
    def userManagement(self, request):
        template = 'users/user_management.html'
        page_title = 'BRDWT | User Management'
        context = {'master_expand': 'nav-expanded', 'page_title': page_title, 'user_management': 'nav-active'}
        if request.method == "POST":
            name = request.POST.get('username')
            district_id = request.POST.get('district')
            password = request.POST.get('password')
            confirm_password = request.POST.get('Confirm_password')
            # password = make_password(request.POST.get('password'))

            if "create_user" in request.POST:
                user_type = request.POST.get('user_type')
                check_password = validate_password(password, confirm_password)
                if not check_password['status']:
                    context['error'] = check_password['message']
                else:
                    if Users.objects.filter(username=name).exists():
                        context['error'] = 'Same username already exist!'
                    else:
                        Users.objects.create_user(user_type=user_type, username=name, district_id=district_id, password=password, is_staff=True, is_active=True).save()
                        context['success'] = 'Successfully Created.'

            elif "update_user" in request.POST:
                code = request.POST.get('code')
                if Users.objects.filter(username=name).exclude(id=code).exists():
                    context['error'] = 'Same username already exist!'
                else:
                    get_data = Users.objects.filter(id=code).first()
                    get_data.username = name
                    get_data.save()
                    context['success'] = 'Successfully Updated.'

            elif "delete" in request.POST:
                code = request.POST.get('code')
                get_data = Users.objects.filter(id=code).first()
                get_data.is_active = False
                get_data.save()
                context['success'] = 'Successfully Deleted.'

        context['user_list'] = Users.objects.filter(is_active=True).all().order_by('id')
        return render(request, template, context)


def userProfile(request):
    template = 'users/user_profile.html'
    page_title = 'BRDWT | User Profile'
    form = PasswordChangeForm(request.user)
    context = {'master_expand': 'nav-expanded', 'page_title': page_title, 'user_management': 'nav-active', 'form': form}
    if request.method == "POST":

        new_password = request.POST.get('new_password1')
        new_password1 = request.POST.get('new_password1')
        check_password = validate_password(new_password, new_password1)
        if not check_password['status']:
            context['msg'] = check_password['message']
            context['error_status'] = check_password['status']
        else:
            form = PasswordChangeForm(request.user, request.POST)
            if form.is_valid():
                user = form.save()
                update_session_auth_hash(request, user)  # Important!
                context['msg'] = 'Successfully Updated.'
            else:
                context['errormsg'] = form
    return render(request, template, context)


class ResetPasswordRequestView(FormView):
    template_name = "users/forgot_password_template.html"
    success_url = '/reset_password/'
    form_class = PasswordResetRequestForm

    @staticmethod
    def validate_email_address(email):
        # This method here validates the if the input is an email address or not. Its return type is boolean, True if the input is a email address or False if its not.
        try:
            validate_email(email)
            return True
        except ValidationError:
            return False

    def post(self, request, *args, **kwargs):
        # A normal post request which takes input from field "email_or_username" (in ResetPasswordRequestForm).
        form = self.form_class(request.POST)
        if form.is_valid():
            data = form.cleaned_data["email_or_username"]

        if self.validate_email_address(data) is True:
            # If the input is an valid email address, then the following code will lookup for users associated with that email address. If found then an email will be sent to the address, else an error message will be printed on the screen.

            associated_users = User.objects.filter(Q(email=data) | Q(username=data))
        else:
            # If the input is an username, then the following code will lookup for users associated with that user. If found then an email will be sent to the user's address, else an error message will be printed on the screen.

            associated_users = User.objects.filter(username=data)
        if associated_users.exists():
            subject_template_name = 'users/password_reset_subject.txt'
            email_template_name = 'users/password_reset_email.html'
            for user in associated_users:
                c = {
                    'email': user.email,
                    'domain': request.META['HTTP_HOST'],
                    'site_name': 'BRDWT',
                    'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                    'user': user,
                    'token': default_token_generator.make_token(user),
                    'protocol': 'http',
                }
                subject = loader.render_to_string(subject_template_name, c)
                # Email subject *must not* contain newlines
                subject = ''.join(subject.splitlines())
                email = loader.render_to_string(email_template_name, c)
                send_mail(subject, email, EMAIL_HOST_USER, [user.email], fail_silently=False)
            result = self.form_valid(form)
            messages.success(request, 'Your password reset is in progress, please check your email for further details')
            return result
        result = self.form_invalid(form)
        messages.error(request, 'Your password reset is in progress, please check your email for further details')
        return result


class PasswordResetConfirmView(FormView):
    template_name = "users/reset_password.html"
    success_url = '/login/'
    form_class = SetPasswordForm

    def post(self, request, uidb64=None, token=None, *arg, **kwargs):
        # View that checks the hash in a password reset link and presents a
        # form for entering a new password.
        UserModel = get_user_model()
        form = self.form_class(request.POST)
        assert uidb64 is not None and token is not None
        try:
            uid = urlsafe_base64_decode(uidb64)
            user = UserModel._default_manager.get(pk=uid)
        except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
            user = None

        if user is not None and default_token_generator.check_token(user, token):
            if form.is_valid():
                new_password = form.cleaned_data['new_password2']
                user.set_password(new_password)
                user.save()
                messages.success(request, 'Password has been reset.')
                return self.form_valid(form)
            else:
                messages.error(request, 'Password reset has not been successful.')
                return self.form_invalid(form)
        else:
            messages.error(request, 'The reset password link is no longer valid.')
            return self.form_invalid(form)


def Logout(request):
    logout(request)
    return redirect('login')
