using EntitlementBLL.Interfaces; using EntitlementDAL.DTOs; using EntitlementDAL.Interfaces; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; namespace EntitlementBLL.Implementations; public class EntitlementAuditBLL : IEntitlementAuditBLL { private readonly IAuditDAL _AuditDAL; public EntitlementAuditBLL(IAuditDAL auditDAL) => _AuditDAL = auditDAL; public async Task> GetEntitlementLogAsync(int clientId, LoginDTO login, CancellationToken ct) => await _AuditDAL.GetEntitlementLogByClientAsync(clientId, login, ct).ConfigureAwait(false); public async Task> GetFlagLogAsync(int featureFlagId, LoginDTO login, CancellationToken ct) => await _AuditDAL.GetFlagLogByFlagAsync(featureFlagId, login, ct).ConfigureAwait(false); public async Task LogActionAsync(int clientId, int subscriptionId, string action, string? remarks, LoginDTO login, CancellationToken ct) { GB5Trace.Step("log-entitlement-action", new { clientId, action }); await _AuditDAL.InsertEntitlementAuditAsync(new EntitlementAuditDTO { ClientId = clientId, SubscriptionId = subscriptionId, Action = action, Remarks = remarks, CreatedById = login.UserId, CreatedOn = DateTime.UtcNow }, login, null, ct).ConfigureAwait(false); } } public class EntitlementCrudBLL : IEntitlementCrudBLL { private readonly IEntitlementGrantDAL _GrantDAL; private readonly IFeatureDAL _FeatureDAL; private readonly IAuditDAL _AuditDAL; private readonly IQueryExecutor _QueryExecutor; public EntitlementCrudBLL(IEntitlementGrantDAL grantDAL, IFeatureDAL featureDAL, IAuditDAL auditDAL, IQueryExecutor queryExecutor) { _GrantDAL = grantDAL; _FeatureDAL = featureDAL; _AuditDAL = auditDAL; _QueryExecutor = queryExecutor; } public async Task> GetFeatureListAsync(LoginDTO login, CancellationToken ct) => await _FeatureDAL.GetListAsync(login, ct).ConfigureAwait(false); public async Task> GetListAsync( int clientId, int featureId, int entitlementSource, bool? isEnabled, int page, int pageSize, LoginDTO login, CancellationToken ct) { var offset = Math.Max(0, (page - 1) * pageSize); var isEnabledFilter = isEnabled.HasValue ? (isEnabled.Value ? 1 : 0) : -1; return await _GrantDAL.GetListPagedAsync( clientId, featureId, entitlementSource, isEnabledFilter, offset, pageSize, login, ct).ConfigureAwait(false); } public async Task SaveAsync(EntitlementDTO dto, LoginDTO login, CancellationToken ct) { if (dto is null) throw new ArgumentNullException(nameof(dto)); if (dto.SubscriptionId <= 0) throw new ArgumentException("SubscriptionId is required.", nameof(dto)); if (dto.FeatureId <= 0) throw new ArgumentException("FeatureId is required.", nameof(dto)); bool isNew = dto.EntitlementId == 0; var tx = await _QueryExecutor.BeginTransactionAsync(login).ConfigureAwait(false); try { GB5Trace.Step("validate-entitlement-grant", new { dto.SubscriptionId, dto.FeatureId, isNew }); var existing = await _GrantDAL.GetBySubscriptionAndFeatureAsync(dto.SubscriptionId, dto.FeatureId, login, ct).ConfigureAwait(false); var oldValue = existing is not null ? $"IsEnabled={existing.IsEnabled}" : null; GB5Trace.Step("save-entitlement-grant", new { dto.SubscriptionId, dto.FeatureId, isNew = existing is null }); if (existing is null) await _GrantDAL.SaveAsync(dto, login, tx, ct).ConfigureAwait(false); else { dto.EntitlementId = existing.EntitlementId; await _GrantDAL.UpdateAsync(dto, login, tx, ct).ConfigureAwait(false); } GB5Trace.Step("audit-entitlement-grant", new { dto.SubscriptionId, dto.FeatureId }); await _AuditDAL.InsertEntitlementAuditAsync(new EntitlementAuditDTO { ClientId = login.ClientId, SubscriptionId = dto.SubscriptionId, FeatureId = dto.FeatureId, Action = existing is null ? "ENTITLEMENT_GRANTED" : "ADMIN_OVERRIDE", OldValue = oldValue, NewValue = $"IsEnabled={dto.IsEnabled}", CreatedById = login.UserId, CreatedOn = DateTime.UtcNow }, login, tx, ct).ConfigureAwait(false); await _QueryExecutor.CommitAsync(tx).ConfigureAwait(false); return existing is null ? $"{SuccessResponse.SaveSuccessMessage} {dto.EntitlementId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { await _QueryExecutor.RollbackAsync(tx).ConfigureAwait(false); GB5Trace.MarkFailed("save-entitlement-grant-failed", ex); throw; } } public async Task DeleteAsync(int entitlementId, LoginDTO login, CancellationToken ct) { var tx = await _QueryExecutor.BeginTransactionAsync(login).ConfigureAwait(false); try { GB5Trace.Step("delete-entitlement-grant", new { entitlementId }); await _GrantDAL.DeleteAsync(entitlementId, login, tx, ct).ConfigureAwait(false); await _QueryExecutor.CommitAsync(tx).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { await _QueryExecutor.RollbackAsync(tx).ConfigureAwait(false); GB5Trace.MarkFailed("delete-entitlement-grant-failed", ex); throw; } } }