using JobTrackerApi.Services; namespace JobTrackerApi.Services.EmailProviders { /// /// Outlook / Microsoft 365 implementation of . Adapts /// (Microsoft Graph client) to the provider-neutral /// contract, mapping Graph DTOs to the neutral shapes. /// public sealed class MicrosoftGraphProvider : IEmailProvider { private readonly IMicrosoftGraphOAuthService _graph; public MicrosoftGraphProvider(IMicrosoftGraphOAuthService graph) { _graph = graph; } public string ProviderKey => "microsoft"; public async Task GetConnectionAsync(string ownerUserId, CancellationToken cancellationToken) { var connection = await _graph.GetConnectionAsync(ownerUserId, cancellationToken); return connection is null ? null : new EmailConnectionInfo("microsoft", connection.MailAddress ?? ""); } public async Task> SearchAsync(string ownerUserId, string? query, int maxResults, CancellationToken cancellationToken) { var messages = await _graph.ListMessagesAsync(ownerUserId, query, maxResults, cancellationToken); return messages.Select(ToSummary).ToList(); } public async Task> ListThreadMessagesAsync(string ownerUserId, string threadId, CancellationToken cancellationToken) { var messages = await _graph.ListThreadMessagesAsync(ownerUserId, threadId, cancellationToken); return messages.Select(ToSummary).ToList(); } public async Task GetMessageAsync(string ownerUserId, string messageId, CancellationToken cancellationToken) { var detail = await _graph.GetMessageAsync(ownerUserId, messageId, cancellationToken); var attachments = detail.Attachments .Select(a => new EmailAttachmentRef(a.FileName, a.MimeType, a.SizeBytes, a.GraphAttachmentId, a.Inline)) .ToList(); return new EmailMessageDetail( detail.Id, detail.ConversationId, detail.Subject, detail.From, detail.To, detail.Date, detail.Snippet, detail.BodyText, detail.BodyHtml, detail.Labels, attachments); } private static EmailMessageSummary ToSummary(MicrosoftGraphMessageSummary m) => new(m.Id, m.ConversationId, m.Subject, m.From, m.To, m.Date, m.Snippet); } }