using Microsoft.Extensions.Caching.Memory;
using OmsorgCore.Application.Services;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Domain.Enums;
using OmsorgCore.Tests.TestDoubles;

namespace OmsorgCore.Tests.Services;

public class PermissionServiceTests
{
    private static User CreateUser(Role role, params UserPermissionOverride[] overrides)
    {
        var user = new User
        {
            Username = "sabrina",
            PasswordHash = "hashed:secret",
            IsActive = true,
            RoleId = role.Id,
            Role = role
        };

        foreach (var override_ in overrides)
        {
            override_.UserId = user.Id;
            override_.User = user;
            user.PermissionOverrides.Add(override_);
        }

        return user;
    }

    [Fact]
    public async Task GetGrantedPermissionsAsync_ReturnsRoleDefaults()
    {
        var role = new Role { Name = "Disposition" };
        role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Orders, Action = PermissionAction.View });
        role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Orders, Action = PermissionAction.Create });

        var user = CreateUser(role);
        var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions()));

        var grants = await sut.GetGrantedPermissionsAsync(user.Id);

        Assert.Contains(grants, g => g.Module == ModuleType.Orders && g.Action == PermissionAction.View);
        Assert.Contains(grants, g => g.Module == ModuleType.Orders && g.Action == PermissionAction.Create);
        Assert.DoesNotContain(grants, g => g.Module == ModuleType.UserManagement);
    }

    [Fact]
    public async Task GetGrantedPermissionsAsync_OverrideRevokesRoleDefault()
    {
        var role = new Role { Name = "Disposition" };
        role.RolePermissions.Add(new RolePermission { Role = role, RoleId = role.Id, Module = ModuleType.Orders, Action = PermissionAction.Delete });

        var user = CreateUser(role, new UserPermissionOverride
        {
            Module = ModuleType.Orders,
            Action = PermissionAction.Delete,
            Effect = PermissionEffect.Revoke
        });
        var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions()));

        var grants = await sut.GetGrantedPermissionsAsync(user.Id);

        Assert.DoesNotContain(grants, g => g.Module == ModuleType.Orders && g.Action == PermissionAction.Delete);
    }

    [Fact]
    public async Task GetGrantedPermissionsAsync_OverrideGrantsBeyondRoleDefault()
    {
        var role = new Role { Name = "Recruiting" };

        var user = CreateUser(role, new UserPermissionOverride
        {
            Module = ModuleType.UserManagement,
            Action = PermissionAction.View,
            Effect = PermissionEffect.Grant
        });
        var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions()));

        var grants = await sut.GetGrantedPermissionsAsync(user.Id);

        Assert.Contains(grants, g => g.Module == ModuleType.UserManagement && g.Action == PermissionAction.View);
    }

    [Fact]
    public async Task HasPermissionAsync_WithoutUserManagementPermission_ReturnsFalse()
    {
        var role = new Role { Name = "Recruiting" };
        var user = CreateUser(role);
        var sut = new PermissionService(new FakeUserRepository(user), new MemoryCache(new MemoryCacheOptions()));

        var allowed = await sut.HasPermissionAsync(user.Id, ModuleType.UserManagement, PermissionAction.View);

        Assert.False(allowed);
    }
}
