using Distribution.Data; using Microsoft.EntityFrameworkCore; namespace Distribution.Features.Notifications; public class NotificationRepository(AppDBContext context) : INotificationRepository { // ── Inbox ──────────────────────────────────────────────────────────── public async Task AddAsync(Models.Notification notification) => await context.Notifications.AddAsync(notification); public void Discard(Models.Notification notification) { foreach (var recipient in notification.Recipients) context.Entry(recipient).State = EntityState.Detached; context.Entry(notification).State = EntityState.Detached; } private IQueryable InboxQuery(Guid userId, bool unreadOnly) { var query = context.NotificationRecipients.Where(r => r.FK_UserId == userId && r.IsActive); return unreadOnly ? query.Where(r => !r.IsRead) : query; } public Task GetInboxCountAsync(Guid userId, bool unreadOnly) => InboxQuery(userId, unreadOnly).CountAsync(); public Task> GetInboxAsync(Guid userId, bool unreadOnly, int page, int pageSize) => InboxQuery(userId, unreadOnly) .Include(r => r.Notification) .OrderByDescending(r => r.Notification.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .AsNoTracking() .ToListAsync(); public Task GetUnreadCountAsync(Guid userId) => InboxQuery(userId, unreadOnly: true).CountAsync(); public Task GetRecipientAsync(Guid userId, Guid recipientId) => context.NotificationRecipients .FirstOrDefaultAsync(r => r.Id == recipientId && r.FK_UserId == userId && r.IsActive); public Task> GetUnreadRecipientsAsync(Guid userId) => InboxQuery(userId, unreadOnly: true).ToListAsync(); public void RemoveRecipient(Models.NotificationRecipient recipient) => context.NotificationRecipients.Remove(recipient); // ── Recipient resolution ───────────────────────────────────────────── public Task> FilterActiveUserIdsAsync(IEnumerable userIds) { var ids = userIds.ToList(); return context.Users .Where(u => u.IsActive && ids.Contains(u.Id)) .Select(u => u.Id) .ToListAsync(); } public Task> GetUserIdsByPermissionAsync(string permissionCode) => context.Users .Where(u => u.IsActive && context.RolePermissions.Any(rp => rp.FK_RoleId == u.FK_RoleId && rp.IsActive && rp.Permission.IsActive && rp.Permission.Code == permissionCode)) .Select(u => u.Id) .ToListAsync(); public async Task> GetUserIdsByScopeAsync(ScopeKind kind, Guid id) { Guid? regionId = null; Guid? routeId = null; Guid? storeId = null; switch (kind) { case ScopeKind.Region: regionId = id; break; case ScopeKind.Route: routeId = id; regionId = await context.Routes.Where(r => r.Id == id) .Select(r => (Guid?)r.FK_RegionId).FirstOrDefaultAsync(); break; case ScopeKind.Store: storeId = id; routeId = await context.Stores.Where(s => s.Id == id) .Select(s => (Guid?)s.FK_RouteId).FirstOrDefaultAsync(); if (routeId is { } rid) regionId = await context.Routes.Where(r => r.Id == rid) .Select(r => (Guid?)r.FK_RegionId).FirstOrDefaultAsync(); break; } var userIds = new HashSet(); if (regionId is { } region) userIds.UnionWith(await context.UserRegionScopes .Where(s => s.IsActive && s.FK_RegionId == region) .Select(s => s.FK_UserId).ToListAsync()); if (routeId is { } route) userIds.UnionWith(await context.UserRouteScopes .Where(s => s.IsActive && s.FK_RouteId == route) .Select(s => s.FK_UserId).ToListAsync()); if (storeId is { } store) userIds.UnionWith(await context.UserStoreScopes .Where(s => s.IsActive && s.FK_StoreId == store) .Select(s => s.FK_UserId).ToListAsync()); return userIds.ToList(); } public Task GetReportsToUserIdAsync(Guid userId) => context.Users.Where(u => u.Id == userId) .Select(u => u.FK_ReportsToUserId) .FirstOrDefaultAsync(); public async Task GetRouteOwnerUserIdAsync(ScopeKind kind, Guid id) { Guid? routeId = kind switch { ScopeKind.Route => id, ScopeKind.Store => await context.Stores.Where(s => s.Id == id) .Select(s => (Guid?)s.FK_RouteId).FirstOrDefaultAsync(), _ => null }; if (routeId is not { } rid) return null; return await context.Routes.Where(r => r.Id == rid && r.IsActive) .Select(r => r.FK_OwnerUserId) .FirstOrDefaultAsync(); } // ── Retention ──────────────────────────────────────────────────────── public Task> GetExpiredReadRecipientsAsync(DateTime cutoff, int take) => context.NotificationRecipients .Where(r => r.IsRead && r.ReadAt != null && r.ReadAt < cutoff) .Take(take) .ToListAsync(); public Task> GetOrphanNotificationsAsync(int take) => context.Notifications .Where(n => !n.Recipients.Any()) .Take(take) .ToListAsync(); public void RemoveRecipients(IEnumerable recipients) => context.NotificationRecipients.RemoveRange(recipients); public void RemoveNotifications(IEnumerable notifications) => context.Notifications.RemoveRange(notifications); }