using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Moq;
using ROLAC.API.Authorization;
using ROLAC.API.Services;
using Xunit;

namespace ROLAC.API.Tests.Authorization;

public class PermissionAuthorizationHandlerTests
{
    private static ClaimsPrincipal UserWithRoles(params string[] roles)
    {
        var claims = roles.Select(role => new Claim("role", role));
        return new ClaimsPrincipal(new ClaimsIdentity(claims, authenticationType: "test"));
    }

    private static async Task<bool> EvaluateAsync(
        ClaimsPrincipal user, PermissionRequirement requirement, IPermissionService permissions)
    {
        var handler = new PermissionAuthorizationHandler(permissions);
        var context = new AuthorizationHandlerContext([requirement], user, resource: null);
        await handler.HandleAsync(context);
        return context.HasSucceeded;
    }

    [Fact]
    public async Task SuperAdmin_AlwaysSucceeds_WithoutConsultingMatrix()
    {
        var permissions = new Mock<IPermissionService>(MockBehavior.Strict); // must NOT be called
        var requirement = new PermissionRequirement(Modules.Members, PermissionActions.Delete);

        var succeeded = await EvaluateAsync(UserWithRoles("super_admin"), requirement, permissions.Object);

        Assert.True(succeeded);
        permissions.Verify(p => p.HasPermissionAsync(It.IsAny<IEnumerable<string>>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never);
    }

    [Fact]
    public async Task RoleWithPermission_Succeeds()
    {
        var permissions = new Mock<IPermissionService>();
        permissions.Setup(p => p.HasPermissionAsync(It.IsAny<IEnumerable<string>>(), Modules.Members, PermissionActions.Write))
                   .ReturnsAsync(true);
        var requirement = new PermissionRequirement(Modules.Members, PermissionActions.Write);

        var succeeded = await EvaluateAsync(UserWithRoles("secretary"), requirement, permissions.Object);

        Assert.True(succeeded);
    }

    [Fact]
    public async Task RoleWithoutPermission_Fails()
    {
        var permissions = new Mock<IPermissionService>();
        permissions.Setup(p => p.HasPermissionAsync(It.IsAny<IEnumerable<string>>(), It.IsAny<string>(), It.IsAny<string>()))
                   .ReturnsAsync(false);
        var requirement = new PermissionRequirement(Modules.Givings, PermissionActions.Write);

        var succeeded = await EvaluateAsync(UserWithRoles("member"), requirement, permissions.Object);

        Assert.False(succeeded);
    }
}
