using CleanArchitecture.Application.Common.Interfaces; using CleanArchitecture.Domain.Users; using CleanArchitecture.Infrastructure.Common; using FluentEmail.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace CleanArchitecture.Infrastructure.Reminders.BackgroundServices; public class ReminderEmailBackgroundService( IServiceScopeFactory serviceScopeFactory, IDateTimeProvider _dateTimeProvider, IFluentEmail _fluentEmail) : IHostedService, IDisposable { private readonly AppDbContext _dbContext = serviceScopeFactory.CreateScope().ServiceProvider.GetRequiredService<AppDbContext>(); private Timer _timer = null!; public Task StartAsync(CancellationToken cancellationToken) { _timer = new Timer(SendEmailNotifications, null, TimeSpan.Zero, TimeSpan.FromMinutes(1)); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { _timer?.Change(Timeout.Infinite, 0); return Task.CompletedTask; } public void Dispose() { _timer?.Dispose(); } /// <summary> /// TODO: there are many edge cases that aren't caught here. This is an immediate nice to have implementation for now. /// </summary> private async void SendEmailNotifications(object? state) { var now = _dateTimeProvider.UtcNow; var oneMinuteFromNow = now.AddMinutes(1); var dueRemindersBySubscription = _dbContext.Reminders .Where(reminder => reminder.DateTime >= now && reminder.DateTime <= oneMinuteFromNow && !reminder.IsDismissed) .GroupBy(reminder => reminder.SubscriptionId) .ToList(); var subscriptionToBeNotified = dueRemindersBySubscription.ConvertAll(x => x.Key); var usersToBeNotified = _dbContext.Users .Where(user => subscriptionToBeNotified.Contains(user.Subscription.Id)) .ToList(); foreach (User? user in usersToBeNotified) { var dueReminders = dueRemindersBySubscription .Single(x => x.Key == user.Subscription.Id) .ToList(); await _fluentEmail .To(user.Email) .Subject($"{dueReminders.Count} reminders due!") .Body($""" Dear {user.FirstName} {user.LastName} from the present. I hope this email finds you well. I'm writing you this email to remind you about the following reminders: {string.Join('\n', dueReminders.Select((reminder, i) => $"{i + 1}. {reminder.Text}"))} Best, {user.FirstName} from the past. """) .SendAsync(); } } }