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

/// <summary>
/// Single source of truth for mapping a user's LDAP groups to the dashboard
/// visibility tags they are granted (<c>MxGateway:Dashboard:GroupToTag</c>).
/// Sibling of <see cref="DashboardGroupRoleMapping"/> and deliberately follows
/// the same group-matching rules (full DN first, leading-RDN fallback,
/// case-insensitive) so operators write one kind of group key for both maps.
/// Because the shared LDAP provider delivers groups already stripped to short
/// RDN names, full-DN <c>GroupToTag</c> keys can never match — use short group
/// names as keys (see the lookup comment in <see cref="MapGroupsToTags"/>).
/// Tags gate dashboard event VISIBILITY only; they are never a data-access
/// constraint.
/// </summary>
internal static class DashboardGroupTagMapping
{
    /// <summary>
    /// Maps the user's LDAP groups to the union of the tags those groups grant.
    /// A group with no entry in the map contributes nothing; duplicate tags
    /// across groups collapse (case-insensitively). Returns an empty set when no
    /// group matches — an empty grant, which the ACL treats as "sees no tagged
    /// session".
    /// </summary>
    /// <param name="groups">The collection of LDAP groups the user belongs to.</param>
    /// <param name="groupToTag">The mapping from group names to granted tags.</param>
    /// <returns>The distinct tags granted across all of the user's groups.</returns>
    internal static IReadOnlySet<string> MapGroupsToTags(
        IEnumerable<string> groups,
        IReadOnlyDictionary<string, string[]> groupToTag)
    {
        HashSet<string> tags = new(StringComparer.OrdinalIgnoreCase);
        if (groupToTag.Count == 0)
        {
            return tags;
        }

        foreach (string group in groups)
        {
            string normalizedGroup = group.Trim();

            // Same lookup semantics as DashboardGroupRoleMapping.MapGroupsToRoles —
            // full literal group string first, leading-RDN value as the fallback, over
            // an OrdinalIgnoreCase map. See the comment there for the consequence that
            // applies verbatim here: the shared ZB.MOM.WW.Auth.Ldap provider delivers
            // groups already stripped to short RDN names, so a full-DN GroupToTag *key*
            // is UNSUPPORTED — it can never equal the short name that arrives. Keep
            // GroupToTag keys as short group names.
            if (!groupToTag.TryGetValue(normalizedGroup, out string[]? granted)
                && !groupToTag.TryGetValue(
                    DashboardGroupRoleMapping.ExtractFirstRdnValue(normalizedGroup),
                    out granted))
            {
                continue;
            }

            if (granted is null)
            {
                continue;
            }

            foreach (string tag in granted)
            {
                tags.Add(tag);
            }
        }

        return tags;
    }
}
