using ZB.MOM.WW.OtOpcUa.Configuration.Entities;

namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;

/// <summary>
///     Deterministic LDAP-group-to-Admin-role mapper driven by <see cref="LdapOptions.GroupToRole"/>.
///     Every returned role corresponds to a group the user actually holds; no inference.
/// </summary>
public static class RoleMapper
{
    /// <summary>Maps LDAP groups to roles using the provided group-to-role mapping dictionary.</summary>
    /// <param name="ldapGroups">The LDAP groups to map.</param>
    /// <param name="groupToRole">The mapping dictionary from LDAP groups to roles.</param>
    /// <returns>The list of roles corresponding to the LDAP groups.</returns>
    public static IReadOnlyList<string> Map(
        IReadOnlyCollection<string> ldapGroups,
        IReadOnlyDictionary<string, string> groupToRole)
    {
        if (groupToRole.Count == 0) return [];

        var roles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (var group in ldapGroups)
        {
            if (groupToRole.TryGetValue(group, out var role))
                roles.Add(role);
        }
        return [.. roles];
    }

    /// <summary>
    ///     Merge the appsettings-derived baseline roles with system-wide DB grants. DB rows are
    ///     additive; cluster-scoped rows (IsSystemWide == false) are ignored under the global model.
    /// </summary>
    /// <param name="baselineRoles">Roles already resolved from appsettings (or the dev stub).</param>
    /// <param name="dbRows">LdapGroupRoleMapping rows for the user's groups (from GetByGroupsAsync).</param>
    /// <returns>The merged, de-duplicated list of roles.</returns>
    public static IReadOnlyList<string> Merge(
        IReadOnlyCollection<string> baselineRoles,
        IReadOnlyCollection<LdapGroupRoleMapping> dbRows)
    {
        var roles = new HashSet<string>(baselineRoles, StringComparer.OrdinalIgnoreCase);
        foreach (var row in dbRows)
            if (row.IsSystemWide)
                roles.Add(row.Role.ToString());
        return [.. roles];
    }
}
