using Kafka.Lib.Auth;
using MassTransit;
using OpenWarehouse.auth.api.Common.CustomIdentifyManager;
using OpenWarehouse.auth.api.Common.Exemptions;
using OpenWarehouse.auth.lib.Model.Role;
using OpenWarehouse.auth.lib.Model.Service;

namespace OpenWarehouse.auth.api.Common.ServiceManager;

public class ServiceManager( 
    UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager, 
    PrivilageManager.PrivilegeManager privilegeManager, ILogger<ServiceManager> logger,
    ITopicProducer<UserChangeMessageType> userTopic) : IServiceManager
{

    public async Task<bool> CheckIfServiceUserExist(string serviceName)
    {
        return await userManager.FindByNameAsync($"Service-{serviceName}") != null;
    }

    public async Task<bool> CreateServiceUserResult(string serviceName, bool serviceIsAdmin = false)
    {
        var serviceUser = new ApplicationUser()
        {
            UserName = $"Service-{serviceName}",
            Email = $"service-{serviceName}@service.local", LockoutEnabled = true,
        };

        var result = await userManager.CreateAsync(serviceUser);
        if (!result.Succeeded) throw new Exception(result.ToString());
        if (serviceIsAdmin) await AddAdminToServiceUser(serviceName);
        
        var addClaim = await userManager.AddClaimAsync(serviceUser, AccountType.Service.Claim());

        var topic = new UserChangeMessageType()
        {
            UserId = serviceUser.Id,
            Changes = new Dictionary<string, string>()
            {
                {"Username", $"Service-{serviceName}"},
                {"AccountType", AccountType.Service.GetValue()},
                {"IsGlobalAdmin", serviceIsAdmin.ToString()}
            }
        };

        _ = userTopic.Produce(topic);
        
        return result.Succeeded && addClaim.Succeeded;
    }

    public async Task<bool> AddAdminToServiceUser(string serviceName)
    {
        var roleName = $"Service-{serviceName}";
        var user = await userManager.FindByNameAsync(roleName) ?? throw new AccountExemptions(false, "Account not found");
        var isAllready = await privilegeManager.UserHasGlobalPrivilage(user, GlobalPrivilage.Privilege.GlobalAdmin);

        if (isAllready) return true;

        isAllready = await roleManager.FindByNameAsync(roleName) != null;

        if (!isAllready)
        {
            var serviceRole = new ApplicationRole()
            {
                IsGlobalAdmin = true,
                Name = roleName
            };
            var serviceRoleResult = await roleManager.CreateAsync(serviceRole);
            if (!serviceRoleResult.Succeeded)
            {
                throw new RoleExemptions(false, serviceRoleResult.ToString(), serviceRoleResult
                    .Errors.Select(error => new KeyValuePair<string, string>(error.Code, error.Description))
                    .ToDictionary());
            }
        }
        
        
        
        var addRes = await userManager.AddToRoleAsync(user, roleName);
        
        var topic = new UserChangeMessageType()
        {
            UserId = user.Id,
            Changes = new Dictionary<string, string>()
            {
                {"Role", isAllready ? "Allready exist" : "Created"},
                {"Operation", "Added admin to user"},
                {"Sucess", addRes.Succeeded.ToString()}
            }
        };

        _ = userTopic.Produce(topic);
        
        return addRes.Succeeded;
    }


    public async Task<List<CreateServiceRoleResult>> CreateServiceRoles(List<RoleModel> serviceRoles,
        bool forcePrivilages = false)
    {
        logger.LogDebug($"Create roles: {serviceRoles.Select(q => q.RoleName).ToList()}");
        var result = new List<CreateServiceRoleResult>();
        foreach (var role in serviceRoles)
        {
            Dictionary<string, string> errors = new();
            var roleApp = new ApplicationRole()
            {
                IsGlobalAdmin = role.IsGlobalAdmin,
                Name = role.RoleName
            };

            foreach (var privilage in role.Privilage ?? throw new Exception("Role is null"))
            {
                RolePrivilage? privilageObj;
                if (forcePrivilages)
                {
                    privilageObj = await privilegeManager.GetPrivilageByName(privilage);
                }
                else
                {
                    privilageObj = await privilegeManager.GetPrivilageByNameIfDontExistCreateAsync(privilage);
                }

                if (privilageObj != null)
                {
                    roleApp.Privilege.Add(privilageObj);
                }
                else
                {
                    errors[privilage] = "Not found";
                    logger.LogWarning($"Privilage {privilage} not found");
                }
            }

            var resultRole = await roleManager.CreateAsync(roleApp);
            if (!resultRole.Succeeded)
            {
                _ = resultRole.Errors.Select(errorL => errors[errorL.Code] = errorL.Description);
            }

            result.Add(new CreateServiceRoleResult(resultRole.Succeeded, errors));
        }

        return result;
    }

    public async Task<List<CreateServicePrivilageResult>> CreateServicesPrivilage(List<string> servicePrivilages)
    {
        List<CreateServicePrivilageResult> results = new();
        foreach (var privilage in servicePrivilages)
        {
            var resultsLocal = await privilegeManager.CreatePrivageAsync(privilage);
            
            logger.LogDebug($"Create new role {privilage} is sucess {resultsLocal.IsSuccessed} with errors {resultsLocal.Errors}");
            
            results.Add(new CreateServicePrivilageResult(privilage, resultsLocal.IsSuccessed));
        }

        return results;
    }
}