using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.EventLogPublish; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using GB5Shared.Validation; using Microsoft.Extensions.Logging; using PERMBLL.Interfaces; using PERMDAL.DTOs; using PERMDAL.Interfaces; using System.Text.Json; using static GB5Shared.GB5Constant.Constant; namespace PERMBLL.Implementations; public class AppraisalPlanBLL : IAppraisalPlanBLL { private readonly IAppraisalPlanDAL _dal; private readonly IValidation _validation; private readonly KeyInvalidate _keyInvalidate; private readonly EventLogPublish _eventLog; private readonly ILogger _logger; public AppraisalPlanBLL( IAppraisalPlanDAL dal, IValidation validation, KeyInvalidate keyInvalidate, EventLogPublish eventLog, ILogger logger) { _dal = dal; _validation = validation; _keyInvalidate = keyInvalidate; _eventLog = eventLog; _logger = logger; } public async Task GetAppraisalPlanAsync(int planId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("get-appraisalplan", new { planId }); var dto = await _dal.GetAppraisalPlanAsync(planId, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(dto); } catch (Exception ex) { GB5Trace.MarkFailed("get-appraisalplan-failed", ex); _logger.LogError(ex, "GetAppraisalPlan failed for AppraisalPlanId {PlanId}", planId); throw; } } public async Task GetAppraisalPlanListAsync( short? status, int page, int pageSize, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("get-appraisalplan-list", new { status, page }); var list = await _dal.GetAppraisalPlanListAsync(status, page, pageSize, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } catch (Exception ex) { GB5Trace.MarkFailed("get-appraisalplan-list-failed", ex); _logger.LogError(ex, "GetAppraisalPlanList failed"); throw; } } public async Task GetAppraisalPlanSelectListAsync(CriteriaDTO criteriaDTO, LoginDTO login, CancellationToken ct) { try { var list = await _dal.GetAppraisalPlanSelectListAsync(criteriaDTO, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } catch (Exception ex) { GB5Trace.MarkFailed("get-appraisalplan-selectlist-failed", ex); _logger.LogError(ex, "GetAppraisalPlanSelectList failed"); throw; } } public async Task SaveAppraisalPlanAsync(AppraisalPlanDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-appraisalplan", new { dto.AppraisalPlanId }); await _validation.NotEmpty(dto.Name, nameof(dto.Name)).ConfigureAwait(false); if (dto.PerformanceTo <= dto.PerformanceFrom) return "PerformanceTo must be after PerformanceFrom."; if (dto.EntryTo <= dto.EntryFrom) return "EntryTo must be after EntryFrom."; bool duplicate = await _dal.NameExistsAsync(dto.Name, dto.AppraisalPlanId, login, ct).ConfigureAwait(false); if (duplicate) return $"Appraisal plan name '{dto.Name}' already exists."; bool isNew = dto.AppraisalPlanId == 0; GB5Trace.Step("save-appraisalplan", new { dto.AppraisalPlanId, isNew }); int planId; if (isNew) { dto.TenantId = login.ClientId; planId = await _dal.InsertAppraisalPlanAsync(dto, login, ct).ConfigureAwait(false); } else { planId = dto.AppraisalPlanId; int rows = await _dal.UpdateAppraisalPlanAsync(dto, login, ct).ConfigureAwait(false); if (rows == 0) return "Plan not found, already activated, or not in Pending status."; } GB5Trace.Step("event-publish", new { EventTypeId = isNew ? EventTypeConstant.SAVEPERMAPPRAISALPLANEVENTTYPEID : EventTypeConstant.SAVEPERMAPPRAISALPLANEVENTTYPEID }); await _eventLog.PublishEventLogAsync( isNew ? "Appraisal Plan Created" : "Appraisal Plan Updated", new { dto.Name }, EventTypeConstant.SAVEPERMAPPRAISALPLANEVENTTYPEID, planId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(planId, EntityConstant.OBJECTPERMAPPRAISALPLAN, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {planId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-appraisalplan-failed", ex); _logger.LogError(ex, "SaveAppraisalPlan failed for Name {Name}", dto.Name); throw; } } public async Task ActivateAppraisalPlanAsync(int planId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("activate-appraisalplan", new { planId }); var plan = await _dal.GetAppraisalPlanAsync(planId, login, ct).ConfigureAwait(false); if (plan is null) return "Appraisal plan not found."; if (plan.Status != 0) return "Only a Pending appraisal plan can be activated."; bool conflictExists = await _dal.ActivePlanExistsForTypeAsync(plan.AppraisalTypeId, planId, login, ct).ConfigureAwait(false); if (conflictExists) return "An active appraisal plan already exists for this appraisal type."; GB5Trace.Step("activate-appraisalplan-db", new { planId }); int rows = await _dal.ActivateAppraisalPlanAsync(planId, login, ct).ConfigureAwait(false); if (rows == 0) return "Appraisal plan could not be activated."; GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.ACTIVATEPERMAPPRAISALPLANEVENTTYPEID }); await _eventLog.PublishEventLogAsync( "Appraisal Plan Activated", new { AppraisalPlanId = planId }, EventTypeConstant.ACTIVATEPERMAPPRAISALPLANEVENTTYPEID, planId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(planId, EntityConstant.OBJECTPERMAPPRAISALPLAN, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("activate-appraisalplan-failed", ex); _logger.LogError(ex, "ActivateAppraisalPlan failed for AppraisalPlanId {PlanId}", planId); throw; } } public async Task CloseAppraisalPlanAsync(int planId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("close-appraisalplan", new { planId }); int rows = await _dal.CloseAppraisalPlanAsync(planId, login, ct).ConfigureAwait(false); if (rows == 0) return "Appraisal plan not found or not in Active status."; GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.CLOSEPERMAPPRAISALPLANEVENTTYPEID }); await _eventLog.PublishEventLogAsync( "Appraisal Plan Closed", new { AppraisalPlanId = planId }, EventTypeConstant.CLOSEPERMAPPRAISALPLANEVENTTYPEID, planId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(planId, EntityConstant.OBJECTPERMAPPRAISALPLAN, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("close-appraisalplan-failed", ex); _logger.LogError(ex, "CloseAppraisalPlan failed for AppraisalPlanId {PlanId}", planId); throw; } } }