using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.EventLogPublish; using GB5Shared.Resource.Response; using static GB5Shared.GB5Constant.Constant; using GB5Shared.Telemetry; using GB5Shared.Validation; using Microsoft.Extensions.Logging; using PERMBLL.Interfaces; using PERMDAL.DTOs; using PERMDAL.Interfaces; using System.Text.Json; namespace PERMBLL.Implementations; public class RatingScaleBLL : IRatingScaleBLL { private readonly IRatingScaleDAL _dal; private readonly IValidation _validation; private readonly KeyInvalidate _keyInvalidate; private readonly EventLogPublish _eventLog; private readonly ILogger _logger; public RatingScaleBLL( IRatingScaleDAL dal, IValidation validation, KeyInvalidate keyInvalidate, EventLogPublish eventLog, ILogger logger) { _dal = dal; _validation = validation; _keyInvalidate = keyInvalidate; _eventLog = eventLog; _logger = logger; } public async Task GetRatingScaleAsync(int id, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("get-ratingscale", new { id }); var dto = await _dal.GetRatingScaleAsync(id, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(dto); } catch (Exception ex) { GB5Trace.MarkFailed("get-ratingscale-failed", ex); _logger.LogError(ex, "GetRatingScale failed for RatingScaleId {Id}", id); throw; } } public async Task GetRatingScaleListAsync(short? status, int page, int pageSize, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("get-ratingscale-list", new { status, page }); var list = await _dal.GetRatingScaleListAsync(status, page, pageSize, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } catch (Exception ex) { GB5Trace.MarkFailed("get-ratingscale-list-failed", ex); _logger.LogError(ex, "GetRatingScaleList failed"); throw; } } public async Task GetAllActiveRatingScalesAsync(CriteriaDTO criteriaDTO, LoginDTO login, CancellationToken ct) { try { var list = await _dal.GetAllActiveRatingScalesAsync(criteriaDTO, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } catch (Exception ex) { GB5Trace.MarkFailed("get-ratingscale-active-failed", ex); _logger.LogError(ex, "GetAllActiveRatingScales failed"); throw; } } public async Task SaveRatingScaleAsync(RatingScaleDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-ratingscale", new { dto.RatingScaleId }); await _validation.NotEmpty(dto.Code, nameof(dto.Code)).ConfigureAwait(false); await _validation.NotEmpty(dto.Name, nameof(dto.Name)).ConfigureAwait(false); await _validation.NotEmpty(dto.GradeLetter, nameof(dto.GradeLetter)).ConfigureAwait(false); if (dto.SlabFrom >= dto.SlabTo) return "SlabFrom must be less than SlabTo."; bool codeDuplicate = await _dal.CodeExistsAsync(dto.Code, dto.RatingScaleId, login, ct).ConfigureAwait(false); if (codeDuplicate) return $"Rating scale code '{dto.Code}' already exists."; bool slabOverlap = await _dal.SlabOverlapsAsync(dto.SlabFrom, dto.SlabTo, dto.RatingScaleId, login, ct).ConfigureAwait(false); if (slabOverlap) return $"Slab range {dto.SlabFrom}–{dto.SlabTo} overlaps an existing active rating scale."; bool isNew = dto.RatingScaleId == 0; GB5Trace.Step("save-ratingscale", new { dto.RatingScaleId, isNew }); int rows; if (isNew) { dto.TenantId = login.ClientId; dto.Status = 1; rows = await _dal.SaveRatingScaleAsync(dto, login, ct).ConfigureAwait(false); } else { rows = await _dal.UpdateRatingScaleAsync(dto, login, ct).ConfigureAwait(false); } if (rows == 0) return "No rows affected — Rating Scale not saved."; GB5Trace.Step("event-publish", new { EventTypeId = isNew ? EventTypeConstant.SAVEPERMRATINGSCALEEVENTTYPEID : EventTypeConstant.UPDATEPERMRATINGSCALEEVENTTYPEID }); await _eventLog.PublishEventLogAsync( isNew ? "Rating Scale Created" : "Rating Scale Updated", dto, isNew ? EventTypeConstant.SAVEPERMRATINGSCALEEVENTTYPEID : EventTypeConstant.UPDATEPERMRATINGSCALEEVENTTYPEID, dto.RatingScaleId, login, ct: ct).ConfigureAwait(false); var saveCacheKey = new CacheKeyGeneration().KeyGeneration(dto.RatingScaleId, EntityConstant.OBJECTPERMRATINGSCALE, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(saveCacheKey).ConfigureAwait(false); return isNew ? SuccessResponse.SaveSuccess : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-ratingscale-failed", ex); _logger.LogError(ex, "SaveRatingScale failed for Code {Code}", dto.Code); throw; } } public async Task DeleteRatingScaleAsync(int id, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-ratingscale", new { id }); int rows = await _dal.DeleteRatingScaleAsync(id, login, ct).ConfigureAwait(false); if (rows == 0) return "Rating Scale not found or already deleted."; GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.DELETEPERMRATINGSCALEEVENTTYPEID }); await _eventLog.PublishEventLogAsync( "Rating Scale Deleted", new { RatingScaleId = id }, EventTypeConstant.DELETEPERMRATINGSCALEEVENTTYPEID, id, login, ct: ct).ConfigureAwait(false); var deleteCacheKey = new CacheKeyGeneration().KeyGeneration(id, EntityConstant.OBJECTPERMRATINGSCALE, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(deleteCacheKey).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-ratingscale-failed", ex); _logger.LogError(ex, "DeleteRatingScale failed for RatingScaleId {Id}", id); throw; } } }