using System.Net.Mime;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using OpenWarehouse.auth.lib.Model;
using OpenWarehouse.auth.lib.Model.Account;
using OpenWarehouse.auth.lib.Model.Role;

namespace OpenWarehouse.auth.api.Controller;

[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("[controller]/[action]")]
public class Role(
    UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager,
    PrivilegeManager privilegeManager) : ControllerBase
{
    [HttpPost]
    
    public async Task<ActionResult<RoleListModel>> GetRoles()
    {
        var roles = await roleManager.Roles.ToListAsync();
        var roleList = new RoleListModel();
        foreach (var role in roles)
        {
            var roleC = new RoleModel() { RoleName = role.Name ?? "", IsGlobalAdmin = role.IsGlobalAdmin };

            var privilage = await privilegeManager.GetRolePrivilage(role);
            if (privilage != null)
                roleC.Privilage = privilage;
            roleList.Roles?.Add(roleC);
        }

        return roleList;
    }

    [HttpPost]
    [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
    public async Task<ActionResult<UserRoleModel>> GetUserRoles([FromBody] UserRoleReqestModel req)
    {
        if (req is { UserIdentify: not null })
        {
            var user = (await userManager.FindByNameAsync(req.UserIdentify) )
                       ?? (await userManager.FindByEmailAsync(req.UserIdentify))
                       ?? (await userManager.FindByIdAsync(req.UserIdentify));
            if (user == null) return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "User not found"});
            var rolesNames = await userManager.GetRolesAsync(user);
            var userRoleModel = new UserRoleModel() { UserId = user.Id, Username = user.UserName, Roles = new List<RoleModel>()};
            foreach (var roleName in rolesNames)
            {
                var role = await roleManager.FindByNameAsync(roleName);
                if (role != null)
                {
                    var roleC = new RoleModel
                    {
                        RoleName = role.Name ?? "", IsGlobalAdmin = role.IsGlobalAdmin,
                        Privilage = await privilegeManager.GetRolePrivilage(role)
                    };
                    userRoleModel.Roles.Add(roleC);
                }
            }

            return Ok(userRoleModel);
        }
        else
        {
            return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "Invalid reqest"});
        }
    }

    [HttpPost]
    [HasPrivilege(GlobalPrivilage.Privilege.GlobalCreateRole)]
    [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
    public async Task<ActionResult<OkModel>> CreateRole([FromBody] CreateRoleModel req)
    {
        if (req is { Name: not null })
        {
            var okModel = new OkModel();
            var role = new ApplicationRole() { Name = req.Name, IsGlobalAdmin = false};
            if (req is { Privilage: not null })
            {
                foreach (var privilageName in req.Privilage)
                {
                    var privilage = req.ForcePrivilage
                        ? await privilegeManager.GetPrivilageByName(privilageName)
                        : await privilegeManager.GetPrivilageByNameIfDontExistCreateAsync(privilageName);
                    if (privilage != null)
                    {
                        role.Privilege.Add(privilage);
                    }
                    else
                    {
                        okModel.Message += $"Privilage {privilageName} not found;";
                    }
                }
            }

            var result = await roleManager.CreateAsync(role);
            if (result.Succeeded)
            {
                okModel.IsSuccess = true;
                return Ok(okModel);
            }
            else
            {
                okModel.Message += string.Join(";", result.Errors.Select(error => error.Description));
                okModel.IsSuccess = false;
                return StatusCode(500, okModel);
            }
            
        }
        else
        {
            return StatusCode(500, new ErrorModel(){StatusCode = 500, Message = "Invalid reqest"});
        }
    }

    [HttpPost]
    [HasPrivilege(GlobalPrivilage.Privilege.GlobalEditUser)]
    [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
    public async Task<ActionResult<OkModel>> AddRoleToUser([FromBody] AddRoleToUserModel req)
    {
        if (req is { UserIdentify: not null, RoleIdentify: not null })
        {


            var user = (await userManager.FindByNameAsync(req.UserIdentify))
                       ?? (await userManager.FindByEmailAsync(req.UserIdentify))
                       ?? (await userManager.FindByIdAsync(req.UserIdentify));
            var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
                       ?? (await roleManager.FindByNameAsync(req.RoleIdentify));

            if (user is null || role is null)
            {
                return Problem("User or Role not found");
            }
            
            var result = await userManager.AddToRoleAsync(user, role.Name);

            if (result.Succeeded)
            {
                var rolePriv = await privilegeManager.GetRolePrivilage(role);
                var roleModel = new RoleModel()
                    { IsGlobalAdmin = role.IsGlobalAdmin, RoleName = role.Name, Privilage = rolePriv };
                return Ok(new OkModel() { Message = "Ok", IsSuccess = true, Value = roleModel });
            }
            else
            {
                return Ok(new OkModel()
                    { Message = string.Join(";", result.Errors.Select(error => error.Description)), IsSuccess = true });
            }
        }else
        {
            return Problem("Invalid request");
        }
    } 

    [HttpPost]
    [HasPrivilege(GlobalPrivilage.Privilege.GlobalEditRole)]
    [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
    public async Task<ActionResult<RoleModel>> EditRolePrivilege([FromBody]EditRoleModel req)
    {
        var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
                   ?? (await roleManager.FindByNameAsync(req.RoleIdentify));
        if (role == null) Ok("Not found!");
        foreach (var addPriv in req.AddPrivilage)
        {
            RolePrivilage? priv;
            if (req.ForceAddedPrivilage)
            {
                priv = await privilegeManager.GetPrivilageByNameIfDontExistCreateAsync(addPriv);
            }
            else
            {
                priv = await privilegeManager.GetPrivilageByName(addPriv);
            }

            if (priv is {Privilage: not null} && role != null) await privilegeManager.AddRolePrivilage(role, priv.Privilage);
        }

        if (req.DeletePrivilage != null)
            foreach (var delPriv in req.DeletePrivilage)
            {
                await privilegeManager.DeleteRolePrivilage(role, delPriv);
            }

        if (req.NewName != null && req.NewName != "string")
        {
            if (role != null) role.Name = req.NewName;
        }


        if (role != null)
        {
            var privilage = await privilegeManager.GetRolePrivilage(role);

            var roleModel = new RoleModel()
                { RoleName = role.Name, IsGlobalAdmin = role.IsGlobalAdmin, Privilage = privilage };
            return Ok(roleModel);
        }


        return Problem();
    }

    [HttpDelete]
    [HasPrivilege(GlobalPrivilage.Privilege.GlobalDeleteRole)]
    [Consumes(MediaTypeNames.Application.Json, otherContentTypes: [MediaTypeNames.Application.Xml])]
    public async Task<ActionResult<OkModel>> DeleteRole([FromBody] DeleteRoleModel req)
    {
        if (req.RoleIdentify != null)
        {
            var role = (await roleManager.FindByIdAsync(req.RoleIdentify))
                       ?? (await roleManager.FindByNameAsync(req.RoleIdentify));
            if (role != null)
            {
                var result = await roleManager.DeleteAsync(role);
                if (result.Succeeded)
                {
                    return Ok(new OkModel() { Message = "", IsSuccess = result.Succeeded });
                }
                else
                {
                    return Ok(new OkModel() { Message = string.Join(";", result.Errors.Select(error => error.Description)), IsSuccess = false });
                }
            }
        }

        return Ok(new OkModel() { Message = "Role not found.", IsSuccess = false });
    }
}