using DigitalData.Core.Abstraction.Application.Repository; using EnvelopeGenerator.Application.Common.Dto; using EnvelopeGenerator.Application.Configuration.Queries; using EnvelopeGenerator.Application.Envelopes.Queries; using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Entities; using EnvelopeGenerator.ServiceHost.Exceptions; using EnvelopeGenerator.ServiceHost.Extensions; using EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument; using GdPicture14; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using System.Data; namespace EnvelopeGenerator.ServiceHost.Jobs; [Obsolete("ActionService is a placeholder service added by copilot. Migrate the actual logic from CommonServices.Jobs")] public class FinalizeDocumentJob(IOptions options, ILogger logger, TempFiles tempFiles, ActionService actionService, PDFBurner pdfBurner, PDFMerger pdfMerger, ReportCreator reportCreator, GdViewer? _gdViewer, LicenseManager licenseManager, IMediator mediator, IRepository envRepo, IRepository docStatusRepo) { private readonly WorkerOptions _options = options.Value; private ConfigDto? _config; private const int CompleteWaitTime = 1; private string _parentFolderUid = string.Empty; private sealed class EnvelopeData { public int EnvelopeId { get; set; } public string EnvelopeUuid { get; set; } = string.Empty; public string DocumentPath { get; set; } = string.Empty; public List AnnotationData { get; set; } = new(); public byte[]? DocAsByte { get; set; } } public bool RethrowOnError { get; set; } = true; public async Task ExecuteAsync(IEnumerable envelopes, CancellationToken cancel = default) { var gdPictureKey = _options.GdPictureLicenseKey; tempFiles.Create(); var jobId = typeof(FinalizeDocumentJob).FullName; _config = await mediator.Send(new ReadDefaultConfigQuery(), cancel); foreach (var envelope in envelopes) { try { await Finalize(envelope, cancel); } catch (Exception ex) { logger.LogWarning(ex, "An unexpected exception was ignored while processing the envelope with ID [{id}].", envelope.Id); if(RethrowOnError) throw; } } } public async Task ExecuteAsync(CancellationToken cancel = default) { var envelopes = await mediator.Send(new ReadEnvelopeQuery() { Status = new () { Include = [EnvelopeStatus.EnvelopeCompletelySigned] }, MinMinutesSinceLastChange = CompleteWaitTime, }, cancel); await ExecuteAsync(envelopes, cancel); } private async Task Finalize(EnvelopeDto envelope, CancellationToken cancel) { var annotations = await docStatusRepo.Where(s => s.EnvelopeId == envelope.Id).Select(s => s.Value).ToListAsync(cancel); var burnedDocument = pdfBurner!.BurnAnnotsToPDF(envelope.Documents?.FirstOrDefault()?.ByteData!, annotations, envelope.Id) ?? throw new ApplicationException("Document could not be finalized"); actionService.CreateReport(envelope, cancel); var report = await reportCreator!.CreateReportAsync(envelope, cancel); var mergedDocument = pdfMerger!.MergeDocuments(burnedDocument, report); var outputDirectoryPath = Path.Combine(_config!.ExportPath, _parentFolderUid); if (!Directory.Exists(outputDirectoryPath)) Directory.CreateDirectory(outputDirectoryPath); var outputFilePath = Path.Combine(outputDirectoryPath, $"{envelope.Uuid}.pdf"); try { File.WriteAllBytes(outputFilePath, mergedDocument); } catch (Exception ex) { throw new ExportDocumentException("Could not export final document to disk. Envelope Id is " + envelope.Id, ex); } var outputFile = await File.ReadAllBytesAsync(outputFilePath, cancel); await envRepo.UpdateAsync(e => e.DocResult = outputFile, e => e.Id == envelope.Id, cancel); SendFinalEmails(envelope); actionService.FinalizeEnvelope(envelope, cancel); } private bool SendFinalEmails(EnvelopeDto envelope) { var mailToCreator = (FinalEmailType)(envelope.FinalEmailToCreator ?? 0); var mailToReceivers = (FinalEmailType)(envelope.FinalEmailToReceivers ?? 0); if (mailToCreator != FinalEmailType.No) SendFinalEmailToCreator(envelope, mailToCreator); else logger?.LogWarning("No SendFinalEmailToCreator - oMailToCreator [{mailToCreator}] <> [{noFinalEmailType}] ", mailToCreator, FinalEmailType.No); if (mailToReceivers != FinalEmailType.No) { SendFinalEmailToReceivers(envelope, mailToReceivers); } else { logger?.LogWarning("No SendFinalEmailToReceivers - oMailToCreator [{mailToReceivers}] <> [{noFinalEmailType}] ", mailToReceivers, FinalEmailType.No); } return true; } private bool SendFinalEmailToCreator(EnvelopeDto envelope, FinalEmailType mailToCreator) { if (actionService.CompleteEnvelope(envelope) == false) { logger?.LogError(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{email}]", envelope.User?.Email); return false; } return true; } private bool SendFinalEmailToReceivers(EnvelopeDto envelope, FinalEmailType mailToReceivers) { foreach (var receiver in envelope.EnvelopeReceivers ?? []) { if (!actionService.CompleteEnvelope(envelope, receiver.Receiver!)) return false; } return true; } }