using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using PSTW_CentralSystem.DBContext;
using PSTW_CentralSystem.Models;
using Newtonsoft.Json;
using System.Text.Json;
using System.Data;
using Microsoft.EntityFrameworkCore;
using System.Reflection;

namespace PSTW_CentralSystem.CustomPolicy
{
    public class RoleModulePolicy : IAuthorizationRequirement
    {

    }
    public class RoleModuleHandler : AuthorizationHandler<RoleModulePolicy>
    {
        private readonly CentralSystemContext _authDBContext;
        private readonly UserManager<UserModel> _userManager;
        private readonly RoleManager<RoleModel> _roleManager;
        private readonly IHttpContextAccessor _httpContextAccessor;
        public RoleModuleHandler( CentralSystemContext authDBContext, UserManager<UserModel> userManager, RoleManager<RoleModel> roleManager, IHttpContextAccessor httpContextAccessor)
        { 
            _authDBContext = authDBContext;
            _userManager = userManager;
            _roleManager = roleManager;
            _httpContextAccessor = httpContextAccessor;
        }
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleModulePolicy requirement)
        {
            // Get the current user
            var currentUser = await _userManager.GetUserAsync(context.User);
            var userRole = await _userManager.GetRolesAsync(currentUser ?? new UserModel());
            var moduleName = _httpContextAccessor.HttpContext?.GetRouteData().Values["controller"]?.ToString();
            var pageName = _httpContextAccessor.HttpContext?.GetRouteData().Values["action"]?.ToString();
            var registeredModule = await _authDBContext.ModuleSettings.Where(x => x.ModuleName == moduleName).ToListAsync();

            if (checkIfSuperAdmin())
            {
                context.Succeed(requirement);
                return;
            }
            else { 
                checkModuleExistOrNot();
                checkModuleHaveRoleOrNot();
            }

            bool checkIfSuperAdmin()
            {
                var superAdminRole = _authDBContext.Roles.Where(r => r.Name == "SuperAdmin").FirstOrDefault();
                var sysAdminRole = _authDBContext.Roles.Where(r => r.Name == "SystemAdmin").FirstOrDefault();
                if (userRole.ToString() != null && userRole.Contains("SuperAdmin") && superAdminRole?.Id == 1)
                {
                    return true;
                }
                else if (userRole.ToString() != null && userRole.Contains("SystemAdmin") && sysAdminRole?.Id == 2)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            void checkModuleExistOrNot()
            {

                if ( moduleName == "Admin")
                {
                    context.Fail();
                    return;
                }
                else if (registeredModule == null)
                {
                    context.Fail();
                    return;
                }
                else 
                { 
                    checkModuleActiveOrNot();
                }
            }

            void checkModuleActiveOrNot()
            {
                foreach (var module in registeredModule)
                {
                    if (module.ModuleStatus == 0)
                    {
                        context.Fail();
                        return;
                    }
                }
            }

            void checkModuleHaveRoleOrNot()
            {
                bool isModuleHaveRole = false;
                foreach (var module in registeredModule)
                {
                    var allowedUserTypes = module?.AllowedUserType ?? "";
                    if (allowedUserTypes == "Public" || userRole.Any(role => allowedUserTypes.Contains(role)))
                    {
                        context.Succeed(requirement);
                        return;
                    }
                    else if (currentUser != null && allowedUserTypes == "Registered User")
                    {
                        isModuleHaveRole = true;
                    }
                    else
                    {
                        isModuleHaveRole = false;
                    }
                }

                if (!isModuleHaveRole)
                {
                    context.Fail();
                    return;
                }
                else
                {
                    checkMethodAndRole();
                }
            }

            void checkMethodAndRole()
            {

                // Load all ModuleSettings and process them in memory
                var moduleSettings = _authDBContext.ModuleSettings.AsEnumerable();

                // Check if the method exists in the module settings
                var isMethodExist = moduleSettings.FirstOrDefault(m => m.MethodAllowedUserType?.Any(mt => mt.MethodName == pageName) == true);


                if (isMethodExist != null) // Check if the method exists which means method is registered
                {
                    var registeredMethod = moduleSettings.Where(m => m.MethodAllowedUserType != null && m.MethodAllowedUserType.Any(mt => mt.MethodName == pageName)).FirstOrDefault();
                    var allowedUserTypes = registeredMethod?.MethodAllowedUserType?.Where(mt => mt.MethodName == pageName).Select(mt => mt.AllowedUserTypesArray).FirstOrDefault() ?? Array.Empty<string>();
                    if (userRole.Any(role => allowedUserTypes.Contains(role)) || allowedUserTypes.Contains("All")) // Check if the user role is allowed, allowing only registered user to access.
                    {
                        context.Succeed(requirement);
                        return;
                    }
                    else
                    {
                        context.Fail();
                        return;
                    }
                }
                else  // No method is registered to allow all method to be accessed
                { 
                    context.Succeed(requirement);
                    return;
                }
            }
        }

    }
}
