using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.JsonWebTokens;

namespace OpenWarehouse.auth.api.Common.PrivilageManager;

/// <summary>
/// Attribute to enforce privilege-based authorization.
/// </summary>
public class HasPrivilegeAttribute : Attribute, IAsyncAuthorizationFilter
{
    private GlobalPrivilage.Privilege[] Privileges { get; }
    
    /// <summary>
    /// Initializes a new instance of the <see cref="HasPrivilegeAttribute"/> class with one or more required privileges.
    /// </summary>
    /// <param name="privileges">The privileges required to access the resource.</param>
    public HasPrivilegeAttribute(params GlobalPrivilage.Privilege[] privileges)
    {
        Privileges = privileges;
    }
    
    /// <summary>
    /// Performs the privilege check during authorization.
    /// </summary>
    /// <param name="context">The authorization filter context.</param>
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        var userActionId = context.HttpContext.User.FindFirstValue(JwtRegisteredClaimNames.Sub);
        
        var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
        var privilegeManager = context.HttpContext.RequestServices.GetRequiredService<PrivilegeManager>();

        if (userActionId != null)
        {
            var userAction = await userManager.FindByIdAsync(userActionId);
            if (userAction == null || 
                !await UserHasAnyRequiredPrivilege(privilegeManager, userAction))
            {
                context.Result = new ContentResult
                {
                    StatusCode = StatusCodes.Status403Forbidden,
                    Content = "Access Denied: You do not have the required privileges to perform this action.",
                    ContentType = "text/plain"
                };
            }
        }
    }

    /// <summary>
    /// Checks if the user has at least one of the required privileges.
    /// </summary>
    /// <param name="privilegeManager">The privilege manager service.</param>
    /// <param name="user">The user to check privileges for.</param>
    /// <returns>True if the user has any required privilege; otherwise, false.</returns>
    private async Task<bool> UserHasAnyRequiredPrivilege(PrivilegeManager privilegeManager, ApplicationUser user)
    {
        foreach (var privilege in Privileges)
        {
            if (await privilegeManager.UserHasGlobalPrivilage(user, privilege))
            {
                return true;
            }
        }
        return false;
    }
}
