using JobTrackerApi.Services; namespace JobTrackerApi.Services.EmailProviders { /// /// Generic IMAP implementation of for mailboxes with no /// dedicated OAuth provider. Adapts to the provider-neutral /// contract, mapping IMAP DTOs to the neutral shapes. /// public sealed class ImapProvider : IEmailProvider { private readonly IImapService _imap; public ImapProvider(IImapService imap) { _imap = imap; } public string ProviderKey => "imap"; public async Task GetConnectionAsync(string ownerUserId, CancellationToken cancellationToken) { var connection = await _imap.GetConnectionAsync(ownerUserId, cancellationToken); return connection is null ? null : new EmailConnectionInfo("imap", connection.Username ?? ""); } public async Task> SearchAsync(string ownerUserId, string? query, int maxResults, CancellationToken cancellationToken) { var messages = await _imap.ListMessagesAsync(ownerUserId, query, maxResults, cancellationToken); return messages.Select(ToSummary).ToList(); } public async Task> ListThreadMessagesAsync(string ownerUserId, string threadId, CancellationToken cancellationToken) { var messages = await _imap.ListThreadMessagesAsync(ownerUserId, threadId, cancellationToken); return messages.Select(ToSummary).ToList(); } public async Task GetMessageAsync(string ownerUserId, string messageId, CancellationToken cancellationToken) { var detail = await _imap.GetMessageAsync(ownerUserId, messageId, cancellationToken); var attachments = detail.Attachments .Select(a => new EmailAttachmentRef(a.FileName, a.MimeType, a.SizeBytes, a.ContentId, a.Inline)) .ToList(); return new EmailMessageDetail( detail.Id, detail.ThreadKey, detail.Subject, detail.From, detail.To, detail.Date, detail.Snippet, detail.BodyText, detail.BodyHtml, detail.Labels, attachments); } private static EmailMessageSummary ToSummary(ImapMessageSummary m) => new(m.Id, m.ThreadKey, m.Subject, m.From, m.To, m.Date, m.Snippet); } }