using OpenWarehouse.auth.lib.Model.Service;

namespace OpenWarehouse.auth.api.Common.PrivilageManager;

public class PrivilegeManager(ApplicationDbContext context,
    RoleManager<ApplicationRole> roleManager,
    UserManager<ApplicationUser> userManager,
    ILogger<PrivilegeManager> logger) : IPrivilageManager
{
    public async Task<List<string?>> GetUserPrivilage(ApplicationUser user)
    {
        var roles = await userManager.GetRolesAsync(user);
        List<string?> privilages = [];

        foreach (var role in roles)
        {
            var roleObj = await context.Roles.Include(applicationRole => applicationRole.Privilege).FirstOrDefaultAsync(applicationRole =>
                applicationRole.NormalizedName.Equals(roleManager.NormalizeKey(role)));
            
            if (roleObj != null && roleObj.IsGlobalAdmin)
                privilages.Add("Admin");

            if (roleObj is { Privilege: not null }) 
                privilages.AddRange(roleObj.Privilege.Select(privilage => privilage?.Privilage).ToList());
        }

        return privilages.Distinct().ToList();
    }

    public async Task<List<string>?> GetRolePrivilage(ApplicationRole role)
    {
        
        List<string>? privilageList = [];
        if (role.IsGlobalAdmin)
        {
            privilageList.Add("Admin");
            return privilageList;
        }

        var privilage = await context.RolePrivilages
            .Where(rolePrivilage => rolePrivilage.Roles.Any(applicationRole => applicationRole == role)).ToListAsync();
        
        if (privilage != null)
            privilageList = privilage.Select(privilage => privilage.Privilage).Distinct().ToList();
        
        return privilageList;
    }

    public async Task AddRolePrivilage(ApplicationRole role, string privilage)
    {
        var privilageObj = await context.RolePrivilages.Include(rolePrivilage => rolePrivilage.Roles)
            .FirstOrDefaultAsync(rolePrivilage => rolePrivilage.Privilage.Equals(privilage));

        if(privilageObj is { Roles: not null }
           && !privilageObj.Roles.Any(applicationRole => applicationRole.Id == role.Id))
            privilageObj.Roles.Add(role);
        await context.SaveChangesAsync();
    }

    public async Task DeleteRolePrivilage(ApplicationRole role, string privilage)
    {
        var privilageToDelete = await context.RolePrivilages
            .Where(rolePrivilage => rolePrivilage.Privilage == privilage &&
                                    rolePrivilage.Roles.Any(applicationRole => applicationRole.Id == role.Id))
            .FirstOrDefaultAsync();

        if (privilageToDelete != null)
        {
            context.RolePrivilages.Remove(privilageToDelete);

            await context.SaveChangesAsync();
        }
    }

    public async Task<bool> RoleHasPrivilage(ApplicationRole role, string privilage)
    {
        if (role.IsGlobalAdmin) return true;
        
        if (role.Privilege == null) return false;
        
        

        return role.Privilege.Any(rolePrivilage => rolePrivilage.Privilage == privilage);
    }

    public async Task<bool> UserHasPrivilage(ApplicationUser user, string privilage)
    {
        var rolesName = await userManager.GetRolesAsync(user);
        foreach (var roleName in rolesName)
        {
            var role = await roleManager.FindByNameAsync(roleName);
            if (role == null) return false;
            return await RoleHasPrivilage(role, privilage);

        }

        return false;
    }

    public async Task<bool> UserHasGlobalPrivilage(ApplicationUser user, GlobalPrivilage.Privilege  privilage)
    {
        var have = false;
        
        foreach (var priv in privilage.GetPrivilegesFor())
        {
            have = await UserHasPrivilage(user, priv);
            if (have) break;
        }

        return have;
    }

    public async Task<RolePrivilage?> GetPrivilageByNameIfDontExistCreateAsync(string privilageName)
    {
        var privilage = await GetPrivilageByName(privilageName);

        if (privilage == null)
        {
            privilage = new RolePrivilage(privilage: privilageName);
            context.RolePrivilages.Add(privilage);
            await context.SaveChangesAsync();
        }

        return privilage;
    }

    public async Task<RolePrivilage?> GetPrivilageByName(string privilage)
    {
       return await context.RolePrivilages.FirstOrDefaultAsync(q => q.Privilage == privilage);
    }

    public async Task<CreateServiceRoleResult> CreatePrivageAsync(string privilage)
    {
        try
        {
            var privilageObj = new RolePrivilage() { Privilage = privilage };

            var entityEntry = context.RolePrivilages.Add(privilageObj);

            if (await context.SaveChangesAsync() > 0) return new CreateServiceRoleResult(isSuccessed: true, null) { };
        }   
        catch (Exception e)
        {
            Dictionary<string, string> errors = new Dictionary<string, string>();

            errors[e.Source ?? "Uknow"] = e.Message;
            return new CreateServiceRoleResult(false, errors);

        }

        return new CreateServiceRoleResult(false, null);
    }
}