using AutoMapper; using DigitalData.MessagingService.Application.Common.Dto.MailSearch; using DigitalData.MessagingService.Application.Common.Interfaces.Repositories; using DigitalData.MessagingService.Domain.Entities; using DigitalData.MessagingService.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace DigitalData.MessagingService.Infrastructure.Repositories; public class ReceivedEmailRepository(MessagingServiceDbContext Context, IMapper Mapper) : Repository(Context, Mapper), IReceivedEmailRepository { public async Task> FindAsync(MailSearchFilter mailSearchFilter, EmailAccount? accountQuery = null, CancellationToken cancellationToken = default) { var query = DbSet.AsNoTracking(); // ── Account filter ───────────────────────────────────────────────────── if (accountQuery is not null) query = query.Where(x => x.AccountId == accountQuery.Id); // ── Flag filters ─────────────────────────────────────────────────────── if (mailSearchFilter.UnseenOnly) query = query.Where(x => !x.IsSeen); // ── Text filters ─────────────────────────────────────────────────────── if (!string.IsNullOrWhiteSpace(mailSearchFilter.SubjectContains)) query = query.Where(x => x.Subject.Contains(mailSearchFilter.SubjectContains)); if (!string.IsNullOrWhiteSpace(mailSearchFilter.SenderContains)) query = query.Where(x => x.From.Contains(mailSearchFilter.SenderContains)); if (!string.IsNullOrWhiteSpace(mailSearchFilter.BodyContains)) query = query.Where(x => x.TextBody.Contains(mailSearchFilter.BodyContains) || x.HtmlBody.Contains(mailSearchFilter.BodyContains)); // ── UID filter ───────────────────────────────────────────────────────── if (mailSearchFilter.Uid is { } uid) { if (uid.Absolute.HasValue) query = query.Where(x => x.Uid == uid.Absolute.Value); else { if (uid.Min.HasValue) query = query.Where(x => x.Uid >= uid.Min.Value); if (uid.Max.HasValue) query = query.Where(x => x.Uid <= uid.Max.Value); } } // ── Date filter ──────────────────────────────────────────────────────── if (mailSearchFilter.Date is { } date) { if (date.After.HasValue) query = query.Where(x => x.Date >= date.After.Value); if (date.Before.HasValue) query = query.Where(x => x.Date <= date.Before.Value); } // ── Attachments ──────────────────────────────────────────────────────── if (mailSearchFilter.WithAttachments) query = query.Include(x => x.Attachments); // ── Sort ─────────────────────────────────────────────────────────────── query = mailSearchFilter.SortOrder == MailSortOrder.OldestFirst ? query.OrderBy(x => x.Date) : query.OrderByDescending(x => x.Date); // ── Limit ────────────────────────────────────────────────────────────── if (mailSearchFilter.MaxCount.HasValue) query = query.Take(mailSearchFilter.MaxCount.Value); // ── RecipientContains: To/Cc are IEnumerable (nvarchar(max)) ── // EF Core cannot translate collection predicates on these columns to SQL. // Materialization is deferred until after other DB-side filters narrow the set. var results = await query.ToListAsync(cancellationToken); if (!string.IsNullOrWhiteSpace(mailSearchFilter.RecipientContains)) results = [.. results .Where(x => x.To.Any(t => t.Contains(mailSearchFilter.RecipientContains, StringComparison.OrdinalIgnoreCase)) || x.Cc.Any(c => c.Contains(mailSearchFilter.RecipientContains, StringComparison.OrdinalIgnoreCase)))]; return results; } }