using Account = FiscalOS.Core.Accounts.Account; using Transaction = FiscalOS.Core.Transactions.Transaction; namespace FiscalOS.Infra.Transactions.Plaid; internal interface IPlaidModifiedTransactionHandler { Task HandleAsync( Account account, IEnumerable modified, CancellationToken ct ); } internal sealed class PlaidModifiedTransactionHandler : IPlaidModifiedTransactionHandler { private readonly ILogger _logger; private readonly AppDbContext _appDbContext; private PlaidModifiedTransactionHandler( ILogger logger, AppDbContext appDbContext ) { _logger = logger; _appDbContext = appDbContext; } public static PlaidModifiedTransactionHandler From( ILogger logger, AppDbContext appDbContext ) { return new(logger, appDbContext); } public static PlaidModifiedTransactionHandler From(IServiceProvider serviceProvider) { return new( serviceProvider.GetRequiredService>(), serviceProvider.GetRequiredService() ); } public async Task HandleAsync(Account account, IEnumerable modified, CancellationToken ct) { var modifiedTransactionIds = modified.Select(t => t.TransactionId); var existingModifiedTransactions = await _appDbContext.Transactions .Where(t => t.Metadata is PlaidTransactionMetadata && modifiedTransactionIds.Contains(((PlaidTransactionMetadata)t.Metadata).PlaidId)) .ToListAsync(ct) .ConfigureAwait(false); var modifiedCount = 0; foreach (var existing in existingModifiedTransactions) { try { if (existing.Metadata is not PlaidTransactionMetadata plaidMetadata) { _logger.LogWarning( "Existing transaction {TransactionId} has non-Plaid metadata. Skipping update for this transaction.", existing.Id ); modifiedCount++; continue; } var plaidModifiedTransaction = modified.FirstOrDefault(t => t.TransactionId == plaidMetadata.PlaidId); if (plaidModifiedTransaction is null) { _logger.LogWarning( "No corresponding modified transaction found in Plaid response for existing transaction {TransactionId}. Skipping update for this transaction.", existing.Id ); modifiedCount++; continue; } var newTransactionData = Transaction.From( existing.Id, existing.UserId, existing.AccountId, plaidModifiedTransaction.Merchant, plaidModifiedTransaction.Description, plaidModifiedTransaction.Amount, plaidModifiedTransaction.PostedDate, plaidMetadata ); _appDbContext.Entry(existing).CurrentValues.SetValues(newTransactionData); modifiedCount++; } catch (Exception ex) { _logger.LogError( ex, "Failed to update transaction {TransactionId} for account {AccountId}", account.Id, existing.Id ); } } _logger.LogInformation("Modified {ModifiedCount} transactions for account {AccountId}", modifiedCount, account.Id); return modifiedCount; } }