using System.Text.Json; using CMGMBLL.Interfaces; using CMGMDAL.DTOs; using CMGMDAL.Interfaces; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.EventLogPublish; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using static GB5Shared.GB5Constant.Constant; using Microsoft.Extensions.Logging; namespace CMGMBLL.Implementations; public class SkillDevPlanBLL : ISkillDevPlanBLL { private readonly ISkillDevPlanDAL _dal; private readonly KeyInvalidate _keyInvalidate; private readonly EventLogPublish _eventLog; private readonly ILogger _logger; public SkillDevPlanBLL(ISkillDevPlanDAL dal, KeyInvalidate keyInvalidate, EventLogPublish eventLog, ILogger logger) { _dal = dal; _keyInvalidate = keyInvalidate; _eventLog = eventLog; _logger = logger; } public async Task GetByIdAsync(int devPlanId, LoginDTO login, CancellationToken ct) { try { var dto = await _dal.GetByIdAsync(devPlanId, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(dto); } catch (Exception ex) { GB5Trace.MarkFailed("get-skilldevplan-failed", ex); _logger.LogError(ex, "GetSkillDevPlan failed for DevPlanId {Id}", devPlanId); throw; } } public async Task GetListAsync(int targetId, short planType, short status, LoginDTO login, CancellationToken ct) { try { var list = await _dal.GetListAsync(targetId, planType, status, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } catch (Exception ex) { GB5Trace.MarkFailed("get-skilldevplan-list-failed", ex); _logger.LogError(ex, "GetSkillDevPlanList failed for TargetId {TargetId}", targetId); throw; } } public async Task SaveAsync(SkillDevPlanDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-skilldevplan", new { dto.DevPlanId }); if (dto.SkillId <= 0) return "Skill is required."; if (dto.TargetId <= 0) return "Target (Employee/Team/Department) is required."; if (dto.TargetProficiencyLevel <= dto.CurrentProficiencyLevel) return "Target proficiency level must be higher than current level."; bool isNew = dto.DevPlanId == 0; GB5Trace.Step("save-skilldevplan", new { dto.DevPlanId, isNew }); int savedId = await _dal.SaveAsync(dto, isNew, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.SAVESKILLDEVPLANEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Skill Development Plan Saved", dto, EventTypeConstant.SAVESKILLDEVPLANEVENTTYPEID, savedId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(dto.TargetId, EntityConstant.OBJECTSKILLDEVPLAN, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-skilldevplan-failed", ex); _logger.LogError(ex, "SaveSkillDevPlan failed for SkillId {SkillId}", dto.SkillId); throw; } } public async Task CompleteAsync(int devPlanId, int actualHours, string? completionNotes, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-complete-skilldevplan", new { devPlanId }); var existing = await _dal.GetByIdAsync(devPlanId, login, ct).ConfigureAwait(false); if (existing is null) return "Skill development plan not found."; if (existing.Status == 2) return "This development plan is already completed."; if (existing.Status == 3) return "Cannot complete a plan that is on hold."; if (actualHours <= 0) return "Actual hours must be provided to complete the plan."; GB5Trace.Step("complete-skilldevplan", new { devPlanId, actualHours }); await _dal.CompleteAsync(devPlanId, actualHours, completionNotes, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.COMPLETESKILLDEVPLANEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Skill Development Plan Completed", new { DevPlanId = devPlanId, ActualHours = actualHours }, EventTypeConstant.COMPLETESKILLDEVPLANEVENTTYPEID, devPlanId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(existing.TargetId, EntityConstant.OBJECTSKILLDEVPLAN, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("complete-skilldevplan-failed", ex); _logger.LogError(ex, "CompleteSkillDevPlan failed for DevPlanId {Id}", devPlanId); throw; } } public async Task DeleteAsync(int devPlanId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-delete-skilldevplan", new { devPlanId }); var existing = await _dal.GetByIdAsync(devPlanId, login, ct).ConfigureAwait(false); if (existing is null) return "Skill development plan not found."; if (existing.Status == 2) return "Cannot delete a completed development plan."; GB5Trace.Step("delete-skilldevplan", new { devPlanId }); await _dal.DeleteAsync(devPlanId, login, ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(existing.TargetId, EntityConstant.OBJECTSKILLDEVPLAN, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-skilldevplan-failed", ex); _logger.LogError(ex, "DeleteSkillDevPlan failed for DevPlanId {Id}", devPlanId); throw; } } }