from django.shortcuts import render, redirect
from django.views import View
from django.contrib.auth.models import User
from django.contrib import messages,auth
from django.http import HttpResponse

from codesofy.custom_config import rename_file,set_user_profile,get_standard_text_input,set_menu_items,get_privilleges,is_authorized,validate_email
from codesofy.custom_config import validate_telephone,validate_mobile,is_super_admin,validate_password,get_max_image_file_size_in_bytes,get_global_master_details
from codesofy.master_details import JobRole,PerPageSelector,UserAccountStatus

from .models import SystemRole, UserProfile #, JobRole
from organizationmanagement.models import Country, Branch
from django.db import transaction, IntegrityError
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db.models import RestrictedError
from django.views.decorators.cache import cache_control
from django.utils.decorators import method_decorator

import os
import math
from io import BytesIO
from PIL import Image
import re
import base64
from binascii import a2b_base64
from django.core.files.uploadedfile import InMemoryUploadedFile
import sys

# Create your views here.

menu_item = "user_management"


def get_active_counsellors():
    counsellors = UserProfile.objects.filter(user__is_active=True)
    return counsellors

def set_sub_menu_item(sub_menu_item,context):
    context = set_menu_items(menu_item,sub_menu_item,context)
    return context


def get_local_master_details():

    context = get_global_master_details()
    countries = Country.objects.all()
    branches = []

    if (len(countries) > 0):

        first_country = countries[0]
        branches = Branch.objects.filter(country=first_country)

    system_roles = SystemRole.objects.all()

    context["countries"] = countries
    context["branches"] = branches
    context["system_roles"] = system_roles
    

    return context


def get_local_master_details_for_user_details():
    context = get_local_master_details()


    user_account_statuses = UserAccountStatus.to_list_for_reports()

    context["user_account_statuses"] = user_account_statuses

    return context


def get_local_master_details_for_add_update_user():

    context = get_local_master_details()
  
    return context


def validate_inputs(request):
        
        username = request.POST['username']
        password = request.POST['password']
        re_typed_password = request.POST['re_typed_password']
        email = request.POST.get('email')
        mobile = request.POST.get('mobile')

        first_name = request.POST['first_name']
        last_name = request.POST['last_name']

        if (not username) or (not password) or (not first_name) or (not last_name):
            messages.error(request,"Backend Validation Failed! Please fill the required fileds")
            return False
        elif ' ' in username:
            messages.error(request,"Backend Validation Failed! Username cannot contain spaces")
            return False
        elif not validate_password(password):
            messages.error(request,"Backend Validation Failed! Password doesn't meet the password complexity requirement")
            return False
        elif first_name.strip()=='':
            messages.error(request,"Backend Validation Failed! Empty First Name")
            return False
        elif last_name.strip()=='':
            messages.error(request,"Backend Validation Failed! Empty Last Name")
            return False
        elif (not re_typed_password):
            messages.error(request,"Backend Validation Failed! Please re-type the password")
            return False
        elif not (password == re_typed_password):
            messages.error(request,"Backend Validation Failed! Mismatch Passwords")
            return False
        elif(mobile and not validate_mobile(mobile)):
            messages.error(request,"Backend Validation Failed! Invalid Mobile Number")
            return False
        elif email and not validate_email(email):
            messages.error(request,"Backend Validation Failed! Invalid Email ID")
            return False
        else:
            return True
        
def validate_inputs_for_update(request):
        

        username = request.POST['username']
        first_name = request.POST['first_name']
        last_name = request.POST['last_name']
        email = request.POST.get('email')
        mobile = request.POST.get('mobile')

        if (not username) or (not first_name) or (not last_name):
            messages.error(request,"Backend Validation Failed! Please fill the required fileds")
            return False
        elif ' ' in username:
            messages.error(request,"Backend Validation Failed! Username cannot contain spaces")
            return False
        elif first_name.strip()=='':
            messages.error(request,"Backend Validation Failed! Empty First Name")
            return False
        elif last_name.strip()=='':
            messages.error(request,"Backend Validation Failed! Empty Last Name")
            return False
        elif(mobile and not validate_mobile(mobile)):
            messages.error(request,"Backend Validation Failed! Invalid Mobile Number")
            return False

        elif email and not validate_email(email):
            messages.error(request,"Backend Validation Failed! Invalid Email ID")
            return False
        else:
            return True
        

class UnauthorizedAccessView(View):
    def get(self,request):
        context = get_global_master_details()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        return render(request, 'unauthorized-access.html', context)

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class ChangePasswordView(View):

    super_featrue = "change_password"
    sub_feature = "change_my_password"
    sub_menu_item = "user_details"

    def get(self, request, id):

        context = get_local_master_details()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if is_authorized(user_profile,self.super_featrue):

            set_sub_menu_item(self.sub_menu_item,context)

            if User.objects.filter(pk=id).exists():
                user = User.objects.get(pk=id)
                user_profile = UserProfile.objects.get(user=user)
                context['user_profile'] = user_profile
                return render(request, 'change-password.html', context)
            else:
                messages.error(request, "Operation Not Allowed")
                return redirect('view-user', id)
        elif is_authorized(user_profile,self.sub_feature):
            user = request.user
            user_profile = UserProfile.objects.get(user=user)
            context['user_profile'] = user_profile
            return render(request, 'change-password.html', context)
        else:
            return redirect("unauthorized-access")

    def post(self, request):

        context = get_local_master_details()

        logged_user_profile = set_user_profile(request,context)

        if logged_user_profile==None:
            return redirect('login')

        get_privilleges(logged_user_profile,context)

        context["old_input_field_values"] = request.POST

        #there should be a branch here... if the user is loggied user then is authorized should be check for that feature. 
        user = None
        user_profile = None
        try:
            with transaction.atomic():
                if is_authorized(logged_user_profile,self.super_featrue):

                    set_sub_menu_item(self.sub_menu_item,context)

                    original_user_id = request.POST.get('original_user_id')

                    user = User.objects.get(pk=original_user_id)
                    user_profile = UserProfile.objects.get(user=user)
                    context['user_profile'] = user_profile
                        
                elif is_authorized(logged_user_profile,self.sub_feature):
                    user = logged_user_profile.user
                    user_profile = logged_user_profile
                    context['user_profile'] = user_profile
                else:
                    return redirect('unauthorized-access')

                password = request.POST.get('password')
                re_typed_password = request.POST.get('re_typed_password')

                if not password:
                    messages.error(request,"Please enter the new password")
                    return render(request, 'change-password.html', context)
                elif not validate_password(password):
                    messages.error(request,"Password doesn't meet the password complexity requirement")
                    return render(request, 'change-password.html', context)
                elif not re_typed_password:
                    messages.error(request,"Please re-type the new password")
                    return render(request, 'change-password.html', context)
                else:
                    if password == re_typed_password:
                        user.set_password(password)
                        user.save()
                        if user.id == logged_user_profile.user.id:
                            print ("validated.... ")
                            #user = auth.authenticate(request=request,password=password)
                            auth.login(request,user,backend='django.contrib.auth.backends.ModelBackend')
                            

                        messages.success(request,"Password has been changed successfully!")
                        return redirect('view-user', user.id)
                    else:
                        messages.error(request,'Backend Validation Failed! Password Mismatch')
                        return render(request, 'change-password.html', context)

        except Exception or IntegrityError as exp:
            messages.error(request, exp)
            messages.error(
                request, "Error Occured in atomic operation. Contact the System Administrator")
            return render(request, 'change-password.html', context)
            
       

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class AddUserView(View):

    this_feature = "add_user"
    sub_menu_item = "add_user"

    def get(self, request):

        context = get_local_master_details_for_add_update_user()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if not is_authorized(user_profile,self.this_feature):
            return redirect('unauthorized-access')
        
        set_sub_menu_item(self.sub_menu_item,context)

        return render(request, 'add-user.html', context)
       

    def post(self, request):
        
        # get data
        # do validations
        # save
        # redirect to view user

        context = get_local_master_details_for_add_update_user()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if not is_authorized(user_profile,self.this_feature):
            return redirect('unauthorized-access')
        
        set_sub_menu_item(self.sub_menu_item,context)


        context["old_input_field_values"] = request.POST

        if (not validate_inputs(request)):
            return render(request, 'add-user.html', context)

        username = request.POST['username']
        profile_picture = request.FILES.get('choose-user-profile-picture')
        captured_image = request.POST.get('captured_image')
        password = request.POST['password']
        system_role_name = request.POST['system_role']
        first_name = request.POST['first_name']
        middle_name = request.POST['middle_name']
        last_name = request.POST['last_name']
        email = request.POST['email']
        mobile = request.POST['mobile']

        print (profile_picture)
        print (type(profile_picture))
        #print (captured_image)


        
        if (profile_picture):
            rename_file(profile_picture, username.lower())
        elif captured_image:
            imgstr = re.search(r'base64,(.*)', captured_image).group(1)
            binary_data = a2b_base64(imgstr)
            file = BytesIO(binary_data)
            new_pic= InMemoryUploadedFile(file, 
            'ImageField',
            username.lower()+'.png',
            'PNG',
            sys.getsizeof(file), None)
            profile_picture = new_pic      
        if profile_picture:
            file_size = profile_picture.size
            if file_size > get_max_image_file_size_in_bytes():
                messages.error(request,"Image file size is too large")
                return render(request, 'add-user.html', context)               

        if not User.objects.filter(username=username.lower()).exists():
            if not email or not User.objects.filter(email=email.lower()).exists():

                user = User(
                    username=username.lower(),
                    first_name=get_standard_text_input(first_name),
                    last_name=get_standard_text_input(last_name), 
                    email=email.lower().strip()
                    )
                user.set_password(password)

                system_role = SystemRole.objects.get(
                    role_name=system_role_name)
                user_profile = UserProfile(
                    user=user,
                    profile_picture=profile_picture,
                    middle_name=get_standard_text_input(middle_name),
                    system_role=system_role,
                    mobile=mobile,
                )

                try:
                    with transaction.atomic():
                        user.save()
                        user_profile.save()
                        messages.success(request, "User is created successfully")
                        return redirect('view-user', id=user.pk)

                except Exception or IntegrityError:
                    messages.error(
                        request, "User is not created. Error Occured in atomic operation. Contact the System Administrator")
                    return render(request, 'add-user.html', context)

            else:
                messages.error(
                    request, "Email is already registered. Please use another email")
                return render(request, 'add-user.html', context)
        else:
            messages.error(
                request, "Username is taken already. Please try another Username")
            return render(request, 'add-user.html', context)

       

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class ViewUserView(View):

    super_feature = "view_user"
    sub_feature = "view_my_profile"
    sub_menu_item = "user_details"

    def get(self, request, id):
        context = get_local_master_details()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)


        if (user_profile.user.id == id):
            if is_authorized(user_profile,self.sub_feature):

                sub_menu_item_2 = "view_my_profile"
                set_sub_menu_item(sub_menu_item_2,context)
                try:
                    with transaction.atomic():

                        context["user_profile"] =user_profile
                        return render(request, 'view-user.html', context)

                except Exception as exp:
                    messages.error(request, exp)
                    return redirect ('user-details')
            else:
                return redirect('unauthorized-access')
        else:
            if is_authorized(user_profile,self.super_feature):

                set_sub_menu_item(self.sub_menu_item,context)

                try:
                    with transaction.atomic():
                        if User.objects.filter(pk=id).exists():
                            user = User.objects.get(pk=id)
                            user_profile = UserProfile.objects.get(user=user)
                            context["user_profile"] =user_profile
                            return render(request, 'view-user.html', context)
                        else:
                            return redirect ('user-details')
                except Exception or IntegrityError:
                    messages.error(
                        request, "Error Occured in atomic operation. Contact the System Administrator")
                    return redirect ('user-details')
            
            else:
                return redirect('unauthorized-access')

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class UpdateUserView(View):

    this_feature = "update_user"
    sub_menu_item = "user_details"


    def get(self, request, id):

        context = get_local_master_details_for_add_update_user()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if not is_authorized(user_profile,self.this_feature):
            return redirect('unauthorized-access')
        
        set_sub_menu_item(self.sub_menu_item,context)


        if User.objects.filter(pk=id).exists():
            user = User.objects.get(pk=id)
            user_profile = UserProfile.objects.get(user=user)

            context["user_profile"] = user_profile

            return render(request, 'update-user.html', context)
        else:
            messages.error(request, "User doesn't exist")
            return render(request, 'update-user.html', context)

    def post(self, request):

        logged_user = request.user

        context = get_local_master_details_for_add_update_user()
        context["sub_menu_item"] = "user_details"

        set_user_profile(request,context)

        context["old_input_field_values"] = request.POST
        original_user_id = request.POST.get('original_user_id')
        original_username = request.POST.get('original_username')

        original_user_temp = User.objects.get(id=original_user_id)
        original_user_profile_temp = UserProfile.objects.get(user=original_user_temp)

        context["user_profile"] = original_user_profile_temp

        try:
            with transaction.atomic():

                original_user = User.objects.get(username=original_username)
                original_user_email = original_user.email
                original_user_profile = UserProfile.objects.get(user=original_user)

                context["user_profile"] = original_user_profile

                if (not validate_inputs_for_update(request)):
                    return render(request, 'update-user.html', context)


                account_active = request.POST.get('account_active',False)
                account_active_status = False

                if account_active=='on':
                    account_active_status = True
                else:
                    account_active_status = False

                if not account_active_status:
                    if logged_user.id == original_user.id:
                        messages.error(request,"Logged in user cannot de-activate the own account")
                        return render(request, 'update-user.html', context)

                username = request.POST['username']
                profile_picture = request.FILES.get('choose-user-profile-picture')
                captured_image = request.POST.get('captured_image')
                system_role_name = request.POST['system_role']
                first_name = request.POST['first_name']
                middle_name = request.POST['middle_name']
                last_name = request.POST['last_name']
                email = request.POST['email']
                mobile = request.POST['mobile']
                
                if (profile_picture):
                    rename_file(profile_picture, username)
                elif captured_image:
                    imgstr = re.search(r'base64,(.*)', captured_image).group(1)
                    binary_data = a2b_base64(imgstr)
                    file = BytesIO(binary_data)
                    new_pic= InMemoryUploadedFile(file, 
                    'ImageField',
                    username.lower()+'.png',
                    'PNG',
                    sys.getsizeof(file), None)
                    profile_picture = new_pic
                if profile_picture:
                    file_size = profile_picture.size
                    if file_size > get_max_image_file_size_in_bytes():
                        messages.error(request,"Image file size is too large")
                        return render(request, 'add-user.html', context)   

                if username.lower() == original_username.lower() or (not User.objects.filter(username=username.lower()).exists()):
                    if not email or email.lower()== original_user_email.lower() or (not User.objects.filter(email=email.lower()).exists()):

                        original_user.username = username.lower()
                        original_user.first_name = get_standard_text_input(first_name)
                        original_user.last_name = get_standard_text_input(last_name)
                        original_user.email = email.lower().strip()
                        original_user.is_active = account_active_status

                        original_user_profile.user = original_user
                        if(profile_picture):
                            original_user_profile.profile_picture = profile_picture
                        original_user_profile.middle_name = get_standard_text_input(middle_name)
                        original_user_profile.system_role = SystemRole.objects.get(
                            role_name=system_role_name)
                        original_user_profile.mobile = mobile
                        original_user.save()
                        original_user_profile.save()
                        messages.success(
                            request, "User details are updated successfully")
                        return redirect('view-user', id=original_user.pk)

                    else:
                        messages.error(
                            request, "Email is already registered. Please use another email")
                        #return redirect ('update-user',original_user.id,context)
                        return render(request, 'update-user.html', context)
                else:
                    messages.error(
                        request, "Username is taken already. Please try another Username")
                    return render(request, 'update-user.html', context)
        except Exception or IntegrityError:
            messages.error(
                request, "Error Occured in atomic operation. Contact the System Administrator")
            return render(request, 'update-user.html', context)

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class DeleteUserView(View):

    this_feature = "delete_user"
    sub_menu_item = "user_details"


    def post(self, request):
        context = {}
        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if not is_authorized(user_profile,self.this_feature):
            return redirect('unauthorized-access')
        
        set_sub_menu_item(self.sub_menu_item,context)

        username = request.POST['username']
        try:
            with transaction.atomic():
                user = User.objects.get(username=username)

                if is_super_admin(user):
                    messages.error(request,"Deletion Restricted! Super Admin account cannot be deleted")
                    return redirect('view-user', user.id)
                else:
                    user.delete()
                return redirect('user-details')
        except RestrictedError:
            messages.error(request,"Deletion Restricted!. User activities are recorded for this user")
            return redirect('view-user', user.id)
        except Exception or IntegrityError:
            messages.error(
                request, "Error Occured in atomic operation. Contact the System Administrator")
            return redirect('view-user', user.id)

@method_decorator(cache_control(no_cache=True, must_revalidate=True,no_store=True), name='dispatch')
class UserDetailsView(View):

    this_feature = "user_details"
    sub_menu_item = "user_details"

    def get(self, request):

        context = get_local_master_details_for_user_details()

        user_profile = set_user_profile(request,context)

        if user_profile==None:
            return redirect('login')

        get_privilleges(user_profile,context)

        if not is_authorized(user_profile,self.this_feature):
            return redirect('unauthorized-access')
        
        set_sub_menu_item(self.sub_menu_item,context)


        per_page_count = request.GET.get('per_page_count')
        page_number = request.GET.get('page_number')
        start_index = request.GET.get('start_index')

        given_name = request.GET.get('given_name')
        user_name = request.GET.get('user_name')

        system_role_id = request.GET.get('system_role_id')
        user_account_status = request.GET.get('user_account_status')

        if not page_number:
            page_number = 1

        if not per_page_count:
            per_page_count = 10

        if start_index:
            page_number = math.ceil(int(start_index)/int(per_page_count))

        context["default_per_page_count"] = per_page_count
        print('page number --- '+ str(page_number))

        #filter logic applies here

        filter_kwargs = {}
    
        if given_name:
            print ('Given Name  - '+ str(given_name))
            given_name = given_name.strip()
            if given_name:
                name_list = given_name.split()
                context["default_given_name"] = " ".join(name_list)
                if len(name_list)>1:
                    first_name = name_list[0]
                    middle_name = name_list[1]
                    filter_kwargs['user__first_name']= first_name
                    filter_kwargs['middle_name__istartswith']= middle_name
                elif len(name_list)>0:
                    first_name = name_list[0]
                    filter_kwargs['user__first_name__istartswith']= first_name
        if user_name:
            
            context["default_user_name"] = user_name
            filter_kwargs["user__username__icontains"]=user_name

        if system_role_id:
            if system_role_id.lower()!="all":
                context["default_system_role_id"] = system_role_id
                filter_kwargs['system_role']= SystemRole.objects.get(pk=system_role_id)

        if user_account_status:
            if user_account_status.lower()!="all":
                context["default_user_account_status"] = UserAccountStatus(user_account_status).value
                if UserAccountStatus(user_account_status).value == UserAccountStatus.ACTIVE.value:
                    filter_kwargs['user__is_active']= True
                else:
                    filter_kwargs['user__is_active']= False

        user_profiles = UserProfile.objects.filter(**filter_kwargs).order_by('id')

        if len(user_profiles)>0:
            context["has_entries"] = True
        else:
            context["has_entries"] = False

        paginator = Paginator(user_profiles,per_page_count)
        page = Paginator.get_page(paginator,page_number)
        page_list = list(paginator.get_elided_page_range(page_number, on_each_side=1))
        
        context["page"] = page
        context["page_list"] = page_list

        return render(request, 'user-details.html', context)
