using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MarcusLaw.OutlookAddin.Core.Models; using Serilog; namespace MarcusLaw.OutlookAddin.Core.Services { public sealed class AttachmentSaveService : IAttachmentSaveService { private readonly IEspoCrmClient _client; private readonly ILogger _logger; public AttachmentSaveService(IEspoCrmClient client, ILogger logger) { _client = client ?? throw new ArgumentNullException(nameof(client)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task SaveAsync( IReadOnlyList attachments, FilingTarget target, string? folderId, CancellationToken cancellationToken = default) { if (attachments == null) throw new ArgumentNullException(nameof(attachments)); if (target == null) throw new ArgumentNullException(nameof(target)); var summary = new AttachmentSaveSummary(); if (attachments.Count == 0) return summary; foreach (var att in attachments) { cancellationToken.ThrowIfCancellationRequested(); if (summary.AuthRequired) { summary.Items.Add(new AttachmentSaveItemResult { FileName = att.Name, Outcome = AttachmentSaveOutcome.AuthRequired, ErrorMessage = "Skipped — earlier upload rejected by EspoCRM" }); continue; } try { var uploaded = await _client.UploadAttachmentAsync( att.Name, att.ContentType, att.ContentBase64, cancellationToken).ConfigureAwait(false); var doc = await _client.CreateDocumentAsync( att.Name, uploaded.Id, target.ParentType, target.ParentId, folderId, cancellationToken).ConfigureAwait(false); _logger.Information( "Saved attachment {Name} → Document/{DocId} (parent={ParentType}/{ParentId}, folder={FolderId})", att.Name, doc.Id, target.ParentType, target.ParentId, folderId ?? "(none)"); summary.Items.Add(new AttachmentSaveItemResult { FileName = att.Name, Outcome = AttachmentSaveOutcome.Saved, DocumentId = doc.Id }); } catch (EspoCrmAuthorizationException ex) { _logger.Warning(ex, "Attachment save aborted: EspoCRM rejected credentials"); summary.AuthRequired = true; summary.Items.Add(new AttachmentSaveItemResult { FileName = att.Name, Outcome = AttachmentSaveOutcome.AuthRequired, ErrorMessage = ex.Message }); } catch (OperationCanceledException) { throw; } catch (Exception ex) { _logger.Warning(ex, "Attachment save failed for {Name}", att.Name); summary.Items.Add(new AttachmentSaveItemResult { FileName = att.Name, Outcome = AttachmentSaveOutcome.Failed, ErrorMessage = ex.Message }); } } return summary; } } }