using Dapper; using JdeScoping.Core.Models.Enums; using JdeScoping.Core.Models.Search; using JdeScoping.DataAccess.Interfaces; using JdeScoping.DataSync.Contracts; using Microsoft.Extensions.Logging; namespace JdeScoping.DataSync.Services; /// /// Repository for Search table operations. /// public class SearchRepository : ISearchRepository { private readonly IDbConnectionFactory _connectionFactory; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The database connection factory. /// The logger instance. public SearchRepository( IDbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// public async Task GetNextQueuedSearchAsync(CancellationToken ct = default) { await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); const string sql = """ SELECT TOP 1 Id, UserName, Name, Status, SubmitDT as SubmitDt, StartDT as StartDt, EndDT as EndDt, CriteriaJSON as CriteriaJson FROM dbo.Search WHERE Status = @Status ORDER BY SubmitDT ASC """; var search = await connection.QueryFirstOrDefaultAsync( sql, new { Status = (int)SearchStatus.Queued }, commandTimeout: 30); if (search != null) { _logger.LogDebug("Found queued search {SearchId}", search.Id); } return search; } /// public async Task ResetPartialSearchesAsync(CancellationToken ct = default) { await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); const string sql = """ UPDATE dbo.Search SET Status = @QueuedStatus, StartDT = NULL WHERE Status = @RunningStatus AND EndDT IS NULL """; var count = await connection.ExecuteAsync( sql, new { QueuedStatus = (int)SearchStatus.Queued, RunningStatus = (int)SearchStatus.Running }, commandTimeout: 30); if (count > 0) { _logger.LogWarning("Reset {Count} partial searches to Queued status", count); } return count; } /// public async Task StartSearchAsync(int searchId, CancellationToken ct = default) { await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); const string sql = """ UPDATE dbo.Search SET Status = @Status, StartDT = @StartDt WHERE Id = @SearchId """; await connection.ExecuteAsync( sql, new { SearchId = searchId, Status = (int)SearchStatus.Running, StartDt = DateTime.UtcNow }, commandTimeout: 30); _logger.LogDebug("Started search {SearchId}", searchId); } /// public async Task CompleteSearchAsync(int searchId, bool success, byte[]? results, CancellationToken ct = default) { await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct); const string sql = """ UPDATE dbo.Search SET Status = @Status, EndDT = @EndDt, Results = @Results WHERE Id = @SearchId """; var status = success ? SearchStatus.Ended : SearchStatus.Error; await connection.ExecuteAsync( sql, new { SearchId = searchId, Status = (int)status, EndDt = DateTime.UtcNow, Results = results }, commandTimeout: 30); _logger.LogDebug("Completed search {SearchId} with status {Status}", searchId, status); } }