using System.Text;
using InboxIntel.Application.Abstractions;
using InboxIntel.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
namespace InboxIntel.Infrastructure.Notifications;
/// Builds an HTML inbox-summary email and sends it via .
public class DigestService : IDigestService
{
private readonly AppDbContext _db;
private readonly IAnalyticsService _analytics;
private readonly IEmailSender _emailSender;
public DigestService(AppDbContext db, IAnalyticsService analytics, IEmailSender emailSender)
{
_db = db;
_analytics = analytics;
_emailSender = emailSender;
}
public async Task SendDigestAsync(Guid userId, CancellationToken ct = default)
{
var user = await _db.Users.FirstOrDefaultAsync(u => u.Id == userId, ct);
if (user is null || string.IsNullOrWhiteSpace(user.Email)) return;
var dashboard = await _analytics.GetDashboardAsync(userId, ct);
var html = BuildHtml(dashboard);
await _emailSender.SendAsync(user.Email, "Your InboxIntel digest", html, ct);
user.LastDigestSentUtc = DateTimeOffset.UtcNow;
await _db.SaveChangesAsync(ct);
}
private static string BuildHtml(Application.DTOs.DashboardSummaryDto d)
{
var sb = new StringBuilder();
sb.Append("
");
sb.Append("
Your inbox this week
");
sb.Append($"
{d.Health.Score}/100 inbox health (grade {d.Health.Grade})
");
sb.Append($"
{d.UnreadEmails:N0} unread of {d.TotalEmails:N0} total emails
");
if (d.Health.SafeToUnsubscribeCount > 0)
sb.Append($"
{d.Health.SafeToUnsubscribeCount} senders are safe to unsubscribe from.
");
if (d.TopSenders.Count > 0)
{
sb.Append("
Top senders
");
foreach (var s in d.TopSenders.Take(5))
sb.Append($"- {System.Net.WebUtility.HtmlEncode(s.DisplayName ?? s.Address)} — {s.EmailCount:N0} emails
");
sb.Append("
");
}
if (d.Health.Recommendations.Count > 0)
{
sb.Append("
Recommendations
");
foreach (var r in d.Health.Recommendations)
sb.Append($"- {System.Net.WebUtility.HtmlEncode(r)}
");
sb.Append("
");
}
sb.Append("
");
return sb.ToString();
}
}