using FrameworkDAL.DTO.EIPConversation; using FrameworkDAL.Query.EIPConversation.EIPOtp; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; namespace FrameworkDAL.CustomCode.EIPConversation.EIPOtp { public class EIPOtpDAL : IEIPOtpDAL { private readonly IQueryExecutor _qe; private readonly ILogger _logger; public EIPOtpDAL(IQueryExecutor qe, ILogger logger) { _qe = qe ?? throw new ArgumentNullException(nameof(qe)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task IssueAsync( int tenantId, string userIdentifier, string capabilityCode, string otpHash, DateTime expiresOn, LoginDTO loginDTO, CancellationToken ct = default) { try { await _qe.ExecuteAsync( loginDTO, EIPOtpQB.SUPERSEDE_PENDING, new { TenantId = tenantId, UserIdentifier = userIdentifier, CapabilityCode = capabilityCode, ModifiedById = loginDTO.UserId }, cancellationToken: ct).ConfigureAwait(false); return await _qe.ExecuteIdentityAsync( loginDTO, EIPOtpQB.INSERT_OTP_RECORD, new { TenantId = tenantId, UserIdentifier = userIdentifier, CapabilityCode = capabilityCode, OtpHash = otpHash, ExpiresOn = expiresOn, CreatedById = loginDTO.UserId, ModifiedById = loginDTO.UserId }).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Failed to issue OTP record | User={User} | Capability={Capability}", userIdentifier, capabilityCode); throw; } } public async Task GetPendingAsync( int tenantId, string userIdentifier, string capabilityCode, LoginDTO loginDTO, CancellationToken ct = default) { try { return await _qe.QuerySingleAsync( loginDTO, EIPOtpQB.GET_PENDING, new { TenantId = tenantId, UserIdentifier = userIdentifier, CapabilityCode = capabilityCode }, cancellationToken: ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Failed to read pending OTP | User={User} | Capability={Capability}", userIdentifier, capabilityCode); throw; } } public async Task MarkVerifiedAsync(int otpRecordId, LoginDTO loginDTO, CancellationToken ct = default) { try { await _qe.ExecuteAsync( loginDTO, EIPOtpQB.MARK_VERIFIED, new { OtpRecordId = otpRecordId, ModifiedById = loginDTO.UserId }, cancellationToken: ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Failed to mark OTP verified | OtpRecordId={OtpRecordId}", otpRecordId); throw; } } public async Task IncrementAttemptAsync( int otpRecordId, int maxAttempts, LoginDTO loginDTO, CancellationToken ct = default) { try { await _qe.ExecuteAsync( loginDTO, EIPOtpQB.INCREMENT_ATTEMPT, new { OtpRecordId = otpRecordId, MaxAttempts = maxAttempts, ModifiedById = loginDTO.UserId }, cancellationToken: ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "Failed to increment OTP attempt count | OtpRecordId={OtpRecordId}", otpRecordId); throw; } } } }