using GerbilManagerWebAPI.Models; using Microsoft.EntityFrameworkCore; namespace GerbilManagerWebAPI.Inbox { /// /// Imports Gmail messages into Request rows: dedup on Message-Id, track the highest IMAP /// UID to avoid rescans. The actual fetch is behind (mockable). /// public sealed class RequestSyncService { private readonly ApplicationContext _db; private readonly IGmailMailReader _reader; private readonly MailSettingsService _settings; public RequestSyncService(ApplicationContext db, IGmailMailReader reader, MailSettingsService settings) { _db = db; _reader = reader; _settings = settings; } public async Task SyncAsync(CancellationToken ct = default) { var settings = await _settings.GetAsync(ct); var password = _settings.DecryptPassword(settings); if (string.IsNullOrWhiteSpace(settings.GmailAddress) || string.IsNullOrWhiteSpace(password)) return new SyncResult(0, "MailNotConfigured"); var conn = new MailConnection(settings.GmailAddress!, password!, settings.Folder); var summaries = await _reader.FetchAsync(conn, settings.LastUid, ct); uint maxUid = settings.LastUid; int imported = 0; if (summaries.Count > 0) { var fetchedIds = summaries.Select(s => s.MessageId).Where(id => !string.IsNullOrEmpty(id)).ToList(); var existing = (await _db.Requests .Where(r => fetchedIds.Contains(r.GmailMessageId)) .Select(r => r.GmailMessageId).ToListAsync(ct)).ToHashSet(); foreach (var m in summaries) { if (m.Uid > maxUid) maxUid = m.Uid; if (string.IsNullOrEmpty(m.MessageId) || !existing.Add(m.MessageId)) continue; _db.Requests.Add(new Request { Id = Guid.NewGuid(), GmailMessageId = m.MessageId, ThreadId = m.ThreadId, InReplyToMessageId = m.InReplyTo, ReferencesHeader = m.References, FromAddress = m.FromAddress, FromName = m.FromName, Subject = m.Subject, BodyText = m.BodyText, ReceivedAt = m.ReceivedAt, Status = RequestStatus.New, }); imported++; } } settings.LastUid = maxUid; await _db.SaveChangesAsync(ct); return new SyncResult(imported, null); } } }