using System; using System.Threading.Tasks; using System.Net.Mail; using System.IO; using ComplianceNFs.Core.Entities; using ComplianceNFs.Core.Ports; namespace ComplianceNFs.Core.Application.Services { // Handles ingestion of invoices from mail attachments public class InvoiceIngestionService : IInvoiceIngestionService { private readonly IMailListener _mailListener; private readonly IAttachmentRepository _attachmentRepository; private readonly IXmlParser _xmlParser; private readonly IPdfParser _pdfParser; public InvoiceIngestionService(IMailListener mailListener, IAttachmentRepository attachmentRepository, IXmlParser xmlParser, IPdfParser pdfParser) { _mailListener = mailListener; _attachmentRepository = attachmentRepository; _xmlParser = xmlParser; _pdfParser = pdfParser; _mailListener.NewMailReceived += OnNewMailReceived; } private async void OnNewMailReceived(MailMessage mail) { // Download attachments, parse, map to EnergyInvoice, save via _attachmentRepository foreach (var attachment in mail.Attachments) { if (attachment is System.Net.Mail.Attachment att && att.Name != null) { using var stream = new MemoryStream(); att.ContentStream.CopyTo(stream); stream.Position = 0; ParsedInvoice parsed = new ParsedInvoice(); if (att.Name.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) parsed = _xmlParser.Parse(stream); else if (att.Name.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) parsed = _pdfParser.Parse(stream); else continue; var invoice = new EnergyInvoice { Filename = att.Name, SupplierEmail = mail.From != null ? mail.From.Address : string.Empty, ReceivedDate = !string.IsNullOrEmpty(mail.Headers?["Date"]) && DateTime.TryParse(mail.Headers["Date"], out var dt) ? dt : DateTime.Now, CnpjComp = parsed.CnpjComp, CnpjVend = parsed.CnpjVend, MontNF = parsed.MontNF, PrecNF = parsed.PrecNF, ValorSemImpostos = parsed.ValorSemImpostos, ValorFinalComImpostos = parsed.ValorFinalComImpostos, RsComp = parsed.RsComp, RsVend = parsed.RsVend, NumeroNF = parsed.NumeroNF, IcmsNF = parsed.IcmsNF, UfComp = parsed.UfComp, UfVend = parsed.UfVend, Status = InvoiceStatus.Pending }; await _attachmentRepository.SaveRawAsync(invoice); } } } public Task IngestAsync() { _mailListener.StartListening(); return Task.CompletedTask; } } // Handles matching logic for invoices public class MatchingService : IMatchingService { private readonly IAccessDbRepository _accessDbRepository; public MatchingService(IAccessDbRepository accessDbRepository) { _accessDbRepository = accessDbRepository; } public Task MatchAsync(EnergyInvoice invoice) { // Example: Primary match logic (simplified) var records = _accessDbRepository.GetByUnidade(invoice.CnpjComp); foreach (var record in records) { if (record.CnpjComp == invoice.CnpjComp && record.CnpjVend == invoice.CnpjVend) { var volMatch = Math.Abs(record.MontLO - invoice.MontNF) / record.MontLO <= 0.01m; var priceMatch = Math.Abs(record.PrecLO - invoice.PrecNF) / record.PrecLO <= 0.005m; if (volMatch && priceMatch) { invoice.MatchedCodTE = record.CodTE; invoice.Status = InvoiceStatus.Matched; break; } } } // TODO: Add fallback and multi-invoice sum logic return Task.CompletedTask; } } // Handles compliance validation public class ComplianceService : IComplianceService { public Task ValidateAsync(EnergyInvoice invoice) { // Example: Tax compliance check if (invoice.Status == InvoiceStatus.Matched || invoice.Status == InvoiceStatus.FallbackMatched) { var impliedTax = invoice.ValorFinalComImpostos / (invoice.ValorSemImpostos == 0 ? 1 : invoice.ValorSemImpostos) - 1; if (Math.Abs(impliedTax - invoice.IcmsNF) > 0.01m) { invoice.Status = InvoiceStatus.TaxMismatch; invoice.DiscrepancyNotes = $"Tax mismatch: implied={impliedTax:P2}, expected={invoice.IcmsNF:P2}"; } else { invoice.Status = InvoiceStatus.Validated; } } return Task.CompletedTask; } } // Handles notifications for mismatches public class NotificationService : INotificationService { public Task NotifyAsync(EnergyInvoice invoice, string message) { // Example: Send notification (placeholder) // In production, use SMTP or other email service Console.WriteLine($"Notify {invoice.SupplierEmail}: {message}"); return Task.CompletedTask; } } // Handles archiving of files public class ArchivingService : IArchivingService { private readonly IFileArchiver _fileArchiver; public ArchivingService(IFileArchiver fileArchiver) { _fileArchiver = fileArchiver; } public Task ArchiveAsync(EnergyInvoice invoice, byte[] rawFile) { return _fileArchiver.ArchiveAsync(invoice, rawFile); } } }