using System.Security.Claims;
using FluentAssertions;
using Microsoft.AspNetCore.Authorization;
using SatelliteProvider.Api.Authentication;

namespace SatelliteProvider.Tests.Authentication;

public class PermissionsRequirementTests
{
    [Fact]
    public void Constructor_RejectsBlankPermission()
    {
        // Act
        var act = () => new PermissionsRequirement(" ");

        // Assert
        act.Should().Throw<ArgumentException>();
    }

    [Fact]
    public async Task Handler_SucceedsWhenSingleStringClaimMatches()
    {
        // Arrange
        var requirement = new PermissionsRequirement(SatellitePermissions.Gps);
        var handler = new PermissionsAuthorizationHandler();
        var user = BuildUser(new Claim(PermissionsAuthorizationHandler.ClaimType, "GPS"));
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);

        // Act
        await handler.HandleAsync(context);

        // Assert
        context.HasSucceeded.Should().BeTrue();
    }

    [Fact]
    public async Task Handler_SucceedsWhenMultipleClaimsContainTarget()
    {
        // Arrange
        var requirement = new PermissionsRequirement(SatellitePermissions.Gps);
        var handler = new PermissionsAuthorizationHandler();
        var user = BuildUser(
            new Claim(PermissionsAuthorizationHandler.ClaimType, "FL"),
            new Claim(PermissionsAuthorizationHandler.ClaimType, "GPS"));
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);

        // Act
        await handler.HandleAsync(context);

        // Assert
        context.HasSucceeded.Should().BeTrue();
    }

    [Fact]
    public async Task Handler_SucceedsWhenSingleClaimEncodesJsonArray()
    {
        // Arrange
        var requirement = new PermissionsRequirement(SatellitePermissions.Gps);
        var handler = new PermissionsAuthorizationHandler();
        var user = BuildUser(new Claim(PermissionsAuthorizationHandler.ClaimType, "[\"FL\",\"GPS\"]"));
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);

        // Act
        await handler.HandleAsync(context);

        // Assert
        context.HasSucceeded.Should().BeTrue();
    }

    [Fact]
    public async Task Handler_DoesNotSucceedWhenClaimIsMissing()
    {
        // Arrange
        var requirement = new PermissionsRequirement(SatellitePermissions.Gps);
        var handler = new PermissionsAuthorizationHandler();
        var user = BuildUser(new Claim(PermissionsAuthorizationHandler.ClaimType, "FL"));
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);

        // Act
        await handler.HandleAsync(context);

        // Assert
        context.HasSucceeded.Should().BeFalse();
    }

    [Fact]
    public async Task Handler_DoesNotSucceedWhenUserIsAnonymous()
    {
        // Arrange
        var requirement = new PermissionsRequirement(SatellitePermissions.Gps);
        var handler = new PermissionsAuthorizationHandler();
        var anon = new ClaimsPrincipal(new ClaimsIdentity());
        var context = new AuthorizationHandlerContext(new[] { requirement }, anon, null);

        // Act
        await handler.HandleAsync(context);

        // Assert
        context.HasSucceeded.Should().BeFalse();
    }

    private static ClaimsPrincipal BuildUser(params Claim[] claims)
    {
        var identity = new ClaimsIdentity(claims, authenticationType: "Test");
        return new ClaimsPrincipal(identity);
    }
}
