using ZB.MOM.WW.MxGateway.Server.Dashboard;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;

/// <summary>
/// Tests for <see cref="DashboardGroupTagMapping"/>, the LDAP-group → dashboard
/// visibility-tag grant. Group matching must follow the same rules as
/// <see cref="DashboardGroupRoleMapping"/> (full DN first, leading-RDN fallback,
/// case-insensitive), and the grant is the union across the user's groups.
/// </summary>
public sealed class DashboardGroupTagMappingTests
{
    private static Dictionary<string, string[]> StandardMapping() => new(StringComparer.OrdinalIgnoreCase)
    {
        ["GwViewer"] = ["team-a"],
        ["TeamBViewers"] = ["team-b", "team-c"],
    };

    /// <summary>Verifies full-DN match, leading-RDN fallback, case-insensitivity, and unmapped → empty.</summary>
    /// <param name="ldapGroup">The LDAP group name or distinguished name.</param>
    /// <param name="expectedTag">The expected single granted tag, or null if no match.</param>
    [Theory]
    [InlineData("GwViewer", "team-a")]
    [InlineData("gwviewer", "team-a")]
    [InlineData("ou=GwViewer,ou=groups,dc=zb,dc=local", "team-a")]
    [InlineData("OtherGroup", null)]
    public void MapGroupsToTags_ResolvesByShortNameAndDistinguishedName(string ldapGroup, string? expectedTag)
    {
        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags([ldapGroup], StandardMapping());

        if (expectedTag is null)
        {
            Assert.Empty(tags);
        }
        else
        {
            Assert.Equal(expectedTag, Assert.Single(tags));
        }
    }

    /// <summary>Verifies the grant is the union of every matching group's tags.</summary>
    [Fact]
    public void MapGroupsToTags_MultipleGroups_UnionsTags()
    {
        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags(
            ["GwViewer", "TeamBViewers"],
            StandardMapping());

        string[] ordered = [.. tags.OrderBy(t => t, StringComparer.Ordinal)];
        Assert.Equal<string>(["team-a", "team-b", "team-c"], ordered);
    }

    /// <summary>Verifies an unknown group contributes nothing to a grant its siblings still produce.</summary>
    [Fact]
    public void MapGroupsToTags_UnknownGroup_ContributesNothing()
    {
        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags(
            ["GwViewer", "NotInTheMap"],
            StandardMapping());

        Assert.Equal("team-a", Assert.Single(tags));
    }

    /// <summary>Verifies the same tag granted by two groups, differing only in case, collapses to one entry.</summary>
    [Fact]
    public void MapGroupsToTags_DuplicateTagsAcrossGroups_DedupedCaseInsensitively()
    {
        Dictionary<string, string[]> mapping = new(StringComparer.OrdinalIgnoreCase)
        {
            ["GroupOne"] = ["team-a"],
            ["GroupTwo"] = ["TEAM-A"],
        };

        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags(["GroupOne", "GroupTwo"], mapping);

        Assert.Single(tags);
        Assert.Contains("team-a", tags);
        Assert.Contains("TEAM-A", tags);
    }

    /// <summary>Verifies an empty map yields an empty grant — no Viewer sees a tagged session.</summary>
    [Fact]
    public void MapGroupsToTags_EmptyMapping_ReturnsNoTags()
    {
        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags(
            ["GwViewer"],
            new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase));

        Assert.Empty(tags);
    }

    /// <summary>
    /// The tag grant is independent of the role map: a group present only in
    /// GroupToTag still grants its tags. Asserted here because the two maps are
    /// deliberately uncoupled in validation as well.
    /// </summary>
    [Fact]
    public void MapGroupsToTags_GroupAbsentFromRoleMap_StillGrantsTags()
    {
        Dictionary<string, string> groupToRole = new(StringComparer.OrdinalIgnoreCase)
        {
            ["GwAdmin"] = DashboardRoles.Admin,
        };

        IReadOnlyList<string> roles = DashboardGroupRoleMapping.MapGroupsToRoles(["TeamBViewers"], groupToRole);
        IReadOnlySet<string> tags = DashboardGroupTagMapping.MapGroupsToTags(["TeamBViewers"], StandardMapping());

        string[] ordered = [.. tags.OrderBy(t => t, StringComparer.Ordinal)];

        Assert.Empty(roles);
        Assert.Equal<string>(["team-b", "team-c"], ordered);
    }
}
