using Microsoft.EntityFrameworkCore; using Nexus.Api.Data; using Nexus.Api.Models; namespace Nexus.Api.Services; public sealed class NotificationService(NexusDbContext db, ILiveUpdateService liveUpdateService) : INotificationService { public async Task CreateAsync(string type, string title, string? message, string forUser, Guid? taskId = null, CancellationToken ct = default) { var notification = new Notification { Type = type, Title = title, Message = message, ForUser = forUser.ToLowerInvariant(), TaskId = taskId }; db.Notifications.Add(notification); await db.SaveChangesAsync(ct); await PublishSnapshotAsync(notification.ForUser, ct); return notification; } public async Task> GetForUserAsync(string forUser, int limit = 50, bool unreadOnly = false, CancellationToken ct = default) { var query = db.Notifications .Where(n => n.ForUser == forUser.ToLowerInvariant()); if (unreadOnly) query = query.Where(n => !n.IsRead); return await query .OrderByDescending(n => n.CreatedAt) .Take(limit) .ToListAsync(ct); } public async Task MarkAsReadAsync(Guid id, CancellationToken ct = default) { var notification = await db.Notifications.FindAsync([id], ct); if (notification is null) return false; notification.IsRead = true; await db.SaveChangesAsync(ct); await PublishSnapshotAsync(notification.ForUser, ct); return true; } public async Task MarkAllAsReadAsync(string forUser, CancellationToken ct = default) { var normalizedUser = forUser.ToLowerInvariant(); var count = await db.Notifications .Where(n => n.ForUser == normalizedUser && !n.IsRead) .ExecuteUpdateAsync(s => s.SetProperty(n => n.IsRead, true), ct); await PublishSnapshotAsync(normalizedUser, ct); return count; } public async Task GetUnreadCountAsync(string forUser, CancellationToken ct = default) { return await db.Notifications .CountAsync(n => n.ForUser == forUser.ToLowerInvariant() && !n.IsRead, ct); } public async Task GetSnapshotAsync(string forUser, int limit = 50, bool unreadOnly = false, CancellationToken ct = default) { var normalizedUser = forUser.ToLowerInvariant(); var notifications = await GetForUserAsync(normalizedUser, limit, unreadOnly, ct); var unreadCount = await GetUnreadCountAsync(normalizedUser, ct); return new NotificationSnapshotDto( notifications.Select(MapToDto).ToList(), unreadCount, normalizedUser); } private async Task PublishSnapshotAsync(string forUser, CancellationToken ct) { var snapshot = await GetSnapshotAsync(forUser, ct: ct); liveUpdateService.Publish("notifications.snapshot", snapshot, "notifications"); } private static NotificationDto MapToDto(Notification n) => new( n.Id, n.Type, n.Title, n.Message, n.ForUser, n.TaskId, n.IsRead, n.CreatedAt); }