from django.shortcuts import render, redirect, get_object_or_404
from django.views import View
from django.contrib.auth.models import User
from django.contrib import messages, auth
from django.http import HttpResponse, JsonResponse
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
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, Q
from django.db import transaction, IntegrityError
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import csrf_protect
from django.core.files.uploadedfile import InMemoryUploadedFile
from datetime import datetime
import json
import os
import math
import re
import sys
from io import BytesIO
from binascii import a2b_base64

from codesofy_lib.custom_config import (
    rename_file, set_user_profile, get_standard_text_input, set_menu_items,
    get_privilleges, is_authorized, validate_email, validate_telephone,
    validate_mobile, is_super_admin, validate_password,
    get_max_image_file_size_in_bytes, get_global_master_details
)
from codesofy_lib.master_details import JobRole, PerPageSelector, UserAccountStatus
from .models import SystemRole, UserProfile, Privilege, MainFeature, SubFeature
from organizationmanagement.models import Country, Branch

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()
    job_roles = JobRole.to_list_for_reports()
    user_account_statuses = UserAccountStatus.to_list_for_reports()
    context["job_roles"] = job_roles
    context["user_account_statuses"] = user_account_statuses
    return context

def get_local_master_details_for_add_update_user():
    context = get_local_master_details()
    job_roles = JobRole.to_list()
    context["job_roles"] = job_roles
    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')
    telephone = request.POST.get('telephone')
    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(telephone and not validate_telephone(telephone)):
        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')
    telephone = request.POST.get('telephone')

    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(telephone and not validate_telephone(telephone)):
        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
        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:
                            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):
        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']
        telephone = request.POST['telephone']
        address = request.POST['address']
        branch_code = request.POST['branch_code']
        employee_number = request.POST['employee_number']
        job_role_name = request.POST['job_role']
        reference_id = request.POST['reference_id']

        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)
                branch = Branch.objects.get(branch_code=branch_code)
                job_role = JobRole(job_role_name.upper()).value

                user_profile = UserProfile(
                    user=user,
                    profile_picture=profile_picture,
                    middle_name=get_standard_text_input(middle_name),
                    system_role=system_role,
                    mobile=mobile,
                    telephone=telephone,
                    address=get_standard_text_input(address),
                    branch=branch,
                    employee_number=get_standard_text_input(employee_number),
                    job_role=job_role,
                    reference_id=reference_id
                )
                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']
                telephone = request.POST['telephone']
                address = request.POST['address']
                branch_code = request.POST['branch_code']
                employee_number = request.POST['employee_number']
                job_role_name = request.POST['job_role']
                reference_id = request.POST['reference_id']

                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_profile.telephone = telephone
                        original_user_profile.address = get_standard_text_input(address)
                        original_user_profile.branch = Branch.objects.get(branch_code=branch_code)
                        original_user_profile.employee_number = get_standard_text_input(employee_number)
                        original_user_profile.job_role = JobRole(job_role_name.upper()).value
                        original_user_profile.reference_id = reference_id
                        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 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')
        job_role_string = request.GET.get('job_role')
        system_role_id = request.GET.get('system_role_id')
        branch_code = request.GET.get('branch_code')
        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

        filter_kwargs = {}
        if 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

        exclude_doctor = False
        if job_role_string:
            if job_role_string.lower() != "all":
                if job_role_string.lower() == "doctor":
                    context["default_job_role"] = JobRole(job_role_string).value
                    filter_kwargs["job_role"] = JobRole(job_role_string).value
                elif job_role_string.lower() == "other":
                    context["default_job_role"] = "OTHER"
                    exclude_doctor = True
                else:
                    context["default_job_role"] = JobRole(job_role_string).value
                    filter_kwargs["job_role"] = JobRole(job_role_string).value

        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 branch_code:
            if branch_code.lower() != "all":
                context["default_branch_code"] = branch_code
                filter_kwargs['branch'] = Branch.objects.get(branch_code=branch_code)

        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)
        if exclude_doctor:
            user_profiles = user_profiles.exclude(job_role=JobRole.DOCTOR.value)

        user_profiles = user_profiles.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)

class RolePermissionsAPIView(View):
    def get(self, request):
        try:
            roles = SystemRole.objects.all().order_by('id')
            roles_data = []
            for role in roles:
                assigned_count = Privilege.objects.filter(system_role=role).count()
                roles_data.append({
                    'id': role.id,
                    'role_name': role.role_name,
                    'assigned_features_count': assigned_count,
                    'badge_class': 'bg-success' if assigned_count > 0 else 'bg-secondary',
                    'badge_text': f'{assigned_count} features' if assigned_count > 0 else 'No features'
                })
            return JsonResponse({'success': True, 'roles_data': roles_data})
        except Exception as e:
            return JsonResponse({'success': False, 'error': str(e)})

class FeaturesAPIView(View):
    def get(self, request):
        try:
            main_features = MainFeature.objects.all().order_by('id')
            features_data = []
            for main_feature in main_features:
                sub_features = SubFeature.objects.filter(main_feature=main_feature).order_by('feature_name')
                features_data.append({
                    'main_feature_id': main_feature.id,
                    'main_feature': main_feature.feature_name,
                    'sub_features': [
                        {
                            'id': sf.id,
                            'code': f"{main_feature.feature_name.upper()}_{sf.feature_name.upper()}",
                            'name': sf.feature_name.replace('_', ' ').title(),
                            'type': sf.feature_type
                        } for sf in sub_features
                    ]
                })
            return JsonResponse({'success': True, 'features': features_data})
        except Exception as e:
            return JsonResponse({'success': False, 'error': str(e)})

class RoleFeaturesAPIView(View):
    def get(self, request, role_id):
        try:
            role = SystemRole.objects.get(id=role_id)
            assigned_privileges = Privilege.objects.filter(system_role=role)
            assigned_feature_codes = []
            for privilege in assigned_privileges:
                main_feature = privilege.sub_feature.main_feature.feature_name.upper()
                sub_feature = privilege.sub_feature.feature_name.upper()
                feature_code = f"{main_feature}_{sub_feature}"
                assigned_feature_codes.append(feature_code)
            return JsonResponse({
                'success': True,
                'role_id': role.id,
                'role_name': role.role_name,
                'assigned_feature_codes': assigned_feature_codes
            })
        except SystemRole.DoesNotExist:
            return JsonResponse({'success': False, 'error': 'Role not found'})
        except Exception as e:
            return JsonResponse({'success': False, 'error': str(e)})

    def post(self, request):
        try:
            data = json.loads(request.body)
            role_id = data.get('role_id')
            feature_codes = data.get('feature_codes', [])
            role = SystemRole.objects.get(id=role_id)
            Privilege.objects.filter(system_role=role).delete()
            privileges_created = 0
            failed_features = []
            main_features = MainFeature.objects.all()
            main_feature_map = {}
            for mf in main_features:
                main_feature_map[mf.feature_name.upper()] = mf

            for feature_code in feature_codes:
                try:
                    found_main_feature = None
                    found_sub_feature_name = None
                    for main_feature_name, main_feature_obj in main_feature_map.items():
                        prefix = main_feature_name + "_"
                        if feature_code.startswith(prefix):
                            found_main_feature = main_feature_obj
                            found_sub_feature_name = feature_code[len(prefix):]
                            break
                    if not found_main_feature:
                        failed_features.append(f"{feature_code}: Main feature not found")
                        continue
                    sub_feature = SubFeature.objects.get(
                        main_feature=found_main_feature,
                        feature_name__iexact=found_sub_feature_name
                    )
                    Privilege.objects.create(system_role=role, sub_feature=sub_feature)
                    privileges_created += 1
                except SubFeature.DoesNotExist:
                    failed_features.append(f"{feature_code}: Sub feature not found")
                    continue
                except Exception as e:
                    failed_features.append(f"{feature_code}: Error - {str(e)}")
                    continue

            actual_privileges_count = Privilege.objects.filter(system_role=role).count()
            response_data = {
                'success': True,
                'message': f'Permissions updated successfully! {privileges_created} features assigned.',
                'privileges_created': privileges_created,
                'total_requested': len(feature_codes),
                'actual_in_database': actual_privileges_count
            }
            if failed_features:
                response_data['warning'] = f'{len(failed_features)} features failed'
                response_data['failed_count'] = len(failed_features)
            return JsonResponse(response_data)
        except SystemRole.DoesNotExist:
            return JsonResponse({'success': False, 'error': 'Role not found'})
        except Exception as e:
            return JsonResponse({'success': False, 'error': f'System error: {str(e)}'})

class CreateRoleAPIView(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            role_name = data.get('role_name')
            description = data.get('description', '')
            if not role_name:
                return JsonResponse({'success': False, 'error': 'Role name is required'})
            if SystemRole.objects.filter(role_name__iexact=role_name).exists():
                return JsonResponse({'success': False, 'error': 'Role already exists'})
            new_role = SystemRole.objects.create(role_name=role_name)
            return JsonResponse({
                'success': True,
                'message': f'Role "{role_name}" created successfully',
                'role_id': new_role.id
            })
        except Exception as e:
            return JsonResponse({'success': False, 'error': str(e)})

class InitializeDataView(View):
    def get(self, request):
        try:
            roles_result = SystemRole.initialize_default_roles()
            features_result = MainFeature.initialize_default_features()
            messages.success(request, f"Data initialized successfully! {roles_result}. {features_result}")
            return redirect('manage-permissions')
        except Exception as e:
            messages.error(request, f"Error initializing data: {str(e)}")
            return redirect('manage-permissions')

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class ManagePermissionsView(View):
    this_feature = "manage_permissions"
    sub_menu_item = "manage_permissions"

    def get(self, request):
        context = get_global_master_details()
        user_profile = set_user_profile(request, context)
        if user_profile is None:
            return redirect('login')
        get_privilleges(user_profile, context)
        set_sub_menu_item(self.sub_menu_item, context)
        system_roles = SystemRole.objects.all().order_by('id')
        roles_data = []
        for role in system_roles:
            assigned_count = Privilege.objects.filter(system_role=role).count()
            roles_data.append({
                'id': role.id,
                'role_name': role.role_name,
                'assigned_features_count': assigned_count,
                'badge_class': 'bg-success' if assigned_count > 0 else 'bg-secondary',
                'badge_text': f'{assigned_count} features' if assigned_count > 0 else 'No features'
            })
        context['roles_data'] = roles_data
        return render(request, 'manage-permissions.html', context)

# ==========================================
# USER PROFILE VIEWS (Using Existing Templates)
# ==========================================

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class MyProfileView(View):
    """View for users to see their own profile - uses view-user.html"""
    def get(self, request):
        context = get_local_master_details()
        user_profile = set_user_profile(request, context)
        if user_profile is None:
            return redirect('login')
        get_privilleges(user_profile, context)
        context['user_profile'] = user_profile
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        context['menu_item'] = 'users'
        context['sub_menu_item'] = 'my_profile'
        return render(request, 'view-user.html', context)

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class MyProfileEditView(View):
    """View for users to edit their own profile - uses update-user.html"""
    def get(self, request):
        context = get_local_master_details()
        user_profile = set_user_profile(request, context)
        if user_profile is None:
            return redirect('login')
        get_privilleges(user_profile, context)
        context['user_profile'] = user_profile
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        context['menu_item'] = 'users'
        context['sub_menu_item'] = 'edit_profile'
        return render(request, 'update-user.html', context)

    def post(self, request):
        user_profile = set_user_profile(request, {})
        if user_profile is None:
            return redirect('login')
        user = request.user
        
        first_name = request.POST.get('first_name')
        last_name = request.POST.get('last_name')
        email = request.POST.get('email')
        middle_name = request.POST.get('middle_name', '')
        mobile = request.POST.get('mobile')
        telephone = request.POST.get('telephone', '')
        address = request.POST.get('address', '')

        if User.objects.filter(email=email).exclude(id=user.id).exists():
            messages.error(request, "Email already in use by another user.")
            return redirect('my_profile_edit')

        user.first_name = first_name
        user.last_name = last_name
        user.email = email
        user.save()

        user_profile.middle_name = middle_name
        user_profile.mobile = mobile
        user_profile.telephone = telephone
        user_profile.address = address

        if request.FILES.get('profile_picture'):
            user_profile.profile_picture = request.FILES.get('profile_picture')

        user_profile.save()
        messages.success(request, "Profile updated successfully!")
        return redirect('my_profile')

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class MyChangePasswordView(View):
    """View for users to change their own password - uses change-password.html"""
    def get(self, request):
        context = get_local_master_details()
        user_profile = set_user_profile(request, context)
        if user_profile is None:
            return redirect('login')
        get_privilleges(user_profile, context)
        context['user_profile'] = user_profile
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        context['menu_item'] = 'users'
        context['sub_menu_item'] = 'change_password'
        return render(request, 'change-password.html', context)

    def post(self, request):
        user_profile = set_user_profile(request, {})
        if user_profile is None:
            return redirect('login')
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)
            messages.success(request, "Password changed successfully!")
            return redirect('my_profile')
        else:
            for error in form.errors.values():
                messages.error(request, error)
            return redirect('my_change_password')

# ==========================================
# ADMIN USER MANAGEMENT VIEWS (Using Existing Templates)
# ==========================================

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class AdminUserListView(View):
    """Admin view to list all users - uses user-details.html"""
    def get(self, request):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        context = get_local_master_details()
        context = set_menu_items('users', 'user_list', context)

        search = request.GET.get('search', '')
        users = User.objects.all()
        if search:
            users = users.filter(
                Q(username__icontains=search) |
                Q(first_name__icontains=search) |
                Q(last_name__icontains=search) |
                Q(email__icontains=search)
            )

        paginator = Paginator(users, 20)
        page = request.GET.get('page', 1)
        users = paginator.get_page(page)

        context['users'] = users
        context['search'] = search
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        user_profile = set_user_profile(request, context)
        context['logged_user_profile'] = user_profile

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

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class AdminUserCreateView(View):
    """Admin view to create new users - uses add-user.html"""
    def get(self, request):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        context = get_local_master_details()
        context = set_menu_items('users', 'user_list', context)
        context['system_roles'] = SystemRole.objects.all()
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        user_profile = set_user_profile(request, context)
        context['logged_user_profile'] = user_profile

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

    def post(self, request):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        username = request.POST.get('username')
        email = request.POST.get('email')
        password = request.POST.get('password')
        first_name = request.POST.get('first_name')
        last_name = request.POST.get('last_name')
        is_active = request.POST.get('is_active') == 'on'
        is_staff = request.POST.get('is_staff') == 'on'
        system_role_id = request.POST.get('system_role')

        if User.objects.filter(username=username).exists():
            messages.error(request, "Username already exists.")
            return redirect('admin_user_create')

        if User.objects.filter(email=email).exists():
            messages.error(request, "Email already exists.")
            return redirect('admin_user_create')

        user = User.objects.create_user(
            username=username,
            email=email,
            password=password,
            first_name=first_name,
            last_name=last_name,
            is_active=is_active,
            is_staff=is_staff
        )

        system_role = None
        if system_role_id:
            system_role = SystemRole.objects.get(id=system_role_id)

        UserProfile.objects.create(
            user=user,
            middle_name=request.POST.get('middle_name', ''),
            mobile=request.POST.get('mobile', ''),
            telephone=request.POST.get('telephone', ''),
            address=request.POST.get('address', ''),
            system_role=system_role,
            employee_number=request.POST.get('employee_number', ''),
            job_role=request.POST.get('job_role', ''),
            reference_id=request.POST.get('reference_id', ''),
            branch_id=request.POST.get('branch') or None,
        )

        messages.success(request, f"User '{username}' created successfully!")
        return redirect('admin_user_list')

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class AdminUserEditView(View):
    """Admin view to edit users - uses update-user.html"""
    def get(self, request, user_id):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        context = get_local_master_details()
        context = set_menu_items('users', 'user_list', context)

        user = get_object_or_404(User, id=user_id)
        user_profile = get_object_or_404(UserProfile, user=user)

        context['edit_user'] = user
        context['edit_profile'] = user_profile
        context['system_roles'] = SystemRole.objects.all()
        context['dt_string'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        logged_user_profile = set_user_profile(request, context)
        context['logged_user_profile'] = logged_user_profile

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

    def post(self, request, user_id):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        user = get_object_or_404(User, id=user_id)
        user_profile = get_object_or_404(UserProfile, user=user)

        username = request.POST.get('username')
        email = request.POST.get('email')
        first_name = request.POST.get('first_name')
        last_name = request.POST.get('last_name')
        is_active = request.POST.get('is_active') == 'on'
        is_staff = request.POST.get('is_staff') == 'on'
        system_role_id = request.POST.get('system_role')

        if User.objects.filter(username=username).exclude(id=user.id).exists():
            messages.error(request, "Username already in use.")
            return redirect('admin_user_edit', user_id=user_id)

        if User.objects.filter(email=email).exclude(id=user.id).exists():
            messages.error(request, "Email already in use.")
            return redirect('admin_user_edit', user_id=user_id)

        user.username = username
        user.email = email
        user.first_name = first_name
        user.last_name = last_name
        user.is_active = is_active
        user.is_staff = is_staff
        user.save()

        system_role = None
        if system_role_id:
            system_role = SystemRole.objects.get(id=system_role_id)

        user_profile.middle_name = request.POST.get('middle_name', '')
        user_profile.mobile = request.POST.get('mobile')
        user_profile.telephone = request.POST.get('telephone', '')
        user_profile.address = request.POST.get('address', '')
        user_profile.system_role = system_role
        user_profile.employee_number = request.POST.get('employee_number', '')
        user_profile.job_role = request.POST.get('job_role', '')
        user_profile.reference_id = request.POST.get('reference_id', '')
        user_profile.branch_id = request.POST.get('branch') or None

        if request.FILES.get('profile_picture'):
            user_profile.profile_picture = request.FILES.get('profile_picture')

        user_profile.save()

        messages.success(request, f"User '{username}' updated successfully!")
        return redirect('admin_user_list')

@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True), name='dispatch')
class AdminUserDeleteView(View):
    """Admin view to delete users"""
    def post(self, request, user_id):
        if not request.user.is_staff:
            messages.error(request, "You don't have permission to access this page.")
            return redirect('welcome')

        if request.user.id == user_id:
            messages.error(request, "You cannot delete your own account.")
            return redirect('admin_user_list')

        user = get_object_or_404(User, id=user_id)
        username = user.username
        user.delete()

        messages.success(request, f"User '{username}' deleted successfully!")
        return redirect('admin_user_list')