namespace Distribution.Features.Notifications; public class NotificationRecipientResolver(INotificationRepository repo) { /// /// Expands the rules into a de-duplicated list of active user ids, never including /// . /// public async Task> ResolveAsync(IEnumerable rules, Guid? actingUserId) { var ids = new HashSet(); foreach (var rule in rules) ids.UnionWith(await ResolveRuleAsync(rule)); if (actingUserId is { } actor) ids.Remove(actor); if (ids.Count == 0) return []; return await repo.FilterActiveUserIdsAsync(ids); } private async Task> ResolveRuleAsync(RecipientRule rule) { switch (rule) { case UsersRule r: return r.Ids; case PermissionRule r: return await repo.GetUserIdsByPermissionAsync(r.Code); case ScopeRule r: return await repo.GetUserIdsByScopeAsync(r.Kind, r.Id); case PermissionInScopeRule r: var withPermission = await repo.GetUserIdsByPermissionAsync(r.Code); var inScope = await repo.GetUserIdsByScopeAsync(r.Kind, r.Id); return withPermission.Intersect(inScope); case ReportsToRule r: var supervisor = await repo.GetReportsToUserIdAsync(r.UserId); return supervisor is { } s ? [s] : []; case RouteOwnerRule r: var owner = await repo.GetRouteOwnerUserIdAsync(r.Kind, r.Id); return owner is { } o ? [o] : []; default: throw new NotSupportedException($"Unknown recipient rule {rule.GetType().Name}."); } } }