using Dapper; using JdeScoping.DataAccess.Interfaces; using JdeScoping.Domain.Models; using Microsoft.Extensions.Logging; namespace JdeScoping.DataAccess.Services; /// /// Service implementation for managing manual data sync requests. /// public sealed class ManualSyncRequestService : IManualSyncRequestService { private readonly IDbConnectionFactory _connectionFactory; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The database connection factory. /// The logger instance. public ManualSyncRequestService( IDbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// public async Task> GetRequestsAsync( bool pendingOnly = false, CancellationToken ct = default) { _logger.LogDebug("Getting manual sync requests (pendingOnly: {PendingOnly})", pendingOnly); const string sqlAll = """ SELECT ID, PipelineName, SyncType, RequestDT, RequestedBy, CompletedDT, CancelDT, CancelledBy, RowVersion FROM dbo.ManualSyncRequest ORDER BY RequestDT DESC """; const string sqlPending = """ SELECT ID, PipelineName, SyncType, RequestDT, RequestedBy, CompletedDT, CancelDT, CancelledBy, RowVersion FROM dbo.ManualSyncRequest WHERE CompletedDT IS NULL AND CancelDT IS NULL ORDER BY RequestDT ASC """; await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); var results = await connection.QueryAsync( pendingOnly ? sqlPending : sqlAll); var list = results.ToList(); _logger.LogDebug("Retrieved {Count} manual sync requests", list.Count); return list; } /// public async Task GetNextPendingRequestAsync(CancellationToken ct = default) { _logger.LogDebug("Getting next pending manual sync request"); const string sql = """ SELECT TOP 1 ID, PipelineName, SyncType, RequestDT, RequestedBy, CompletedDT, CancelDT, CancelledBy, RowVersion FROM dbo.ManualSyncRequest WHERE CompletedDT IS NULL AND CancelDT IS NULL ORDER BY RequestDT ASC """; await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); var result = await connection.QueryFirstOrDefaultAsync(sql); if (result != null) { _logger.LogDebug("Found pending request ID {Id} for pipeline {Pipeline}", result.Id, result.PipelineName); } else { _logger.LogDebug("No pending manual sync requests found"); } return result; } /// public async Task CreateRequestAsync( string pipelineName, string syncType, string requestedBy, CancellationToken ct = default) { _logger.LogInformation( "Creating manual sync request for pipeline {Pipeline}, type {SyncType}, by {User}", pipelineName, syncType, requestedBy); const string sql = """ INSERT INTO dbo.ManualSyncRequest (PipelineName, SyncType, RequestedBy) OUTPUT INSERTED.ID, INSERTED.PipelineName, INSERTED.SyncType, INSERTED.RequestDT, INSERTED.RequestedBy, INSERTED.CompletedDT, INSERTED.CancelDT, INSERTED.CancelledBy, INSERTED.RowVersion VALUES (@PipelineName, @SyncType, @RequestedBy) """; await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); var result = await connection.QuerySingleAsync(sql, new { PipelineName = pipelineName, SyncType = syncType, RequestedBy = requestedBy }); _logger.LogInformation("Created manual sync request with ID {Id}", result.Id); return result; } /// public async Task CancelRequestAsync( int id, string cancelledBy, byte[] rowVersion, CancellationToken ct = default) { _logger.LogInformation( "Cancelling manual sync request ID {Id} by {User}", id, cancelledBy); const string sql = """ UPDATE dbo.ManualSyncRequest SET CancelDT = @CancelDT, CancelledBy = @CancelledBy WHERE ID = @Id AND RowVersion = @RowVersion AND CompletedDT IS NULL AND CancelDT IS NULL """; await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); var affectedRows = await connection.ExecuteAsync(sql, new { Id = id, CancelDT = DateTime.UtcNow, CancelledBy = cancelledBy, RowVersion = rowVersion }); var success = affectedRows > 0; if (success) { _logger.LogInformation("Successfully cancelled manual sync request ID {Id}", id); } else { _logger.LogWarning( "Failed to cancel manual sync request ID {Id} - already completed/cancelled or version mismatch", id); } return success; } /// public async Task CompleteRequestAsync( int id, byte[] rowVersion, CancellationToken ct = default) { _logger.LogInformation("Completing manual sync request ID {Id}", id); const string sql = """ UPDATE dbo.ManualSyncRequest SET CompletedDT = @CompletedDT WHERE ID = @Id AND RowVersion = @RowVersion AND CompletedDT IS NULL AND CancelDT IS NULL """; await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); var affectedRows = await connection.ExecuteAsync(sql, new { Id = id, CompletedDT = DateTime.UtcNow, RowVersion = rowVersion }); var success = affectedRows > 0; if (success) { _logger.LogInformation("Successfully completed manual sync request ID {Id}", id); } else { _logger.LogWarning( "Failed to complete manual sync request ID {Id} - already completed/cancelled or version mismatch", id); } return success; } }