using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.Resource.Response; using GB5Shared.Validation; using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; using TMSDAL.CustomCode.Assessment; using TMSDAL.DTO.Assessment; using TMSDAL.DTO.AssessmentAttempt; using TMSDAL.Enums; using static GB5Shared.GB5Constant.Constant; namespace TMSBLL.Assessment { public class AssessmentBLL : IAssessmentBLL { private readonly IAssessmentDAL _AssessmentDAL; private readonly AutoNumber _AutoNumber; private readonly IValidation _Validation; private readonly KeyInvalidate _KeyInvalidate; public AssessmentBLL(IAssessmentDAL assessmentDAL, AutoNumber autoNumber, IValidation validation, KeyInvalidate keyInvalidate) { _AssessmentDAL = assessmentDAL; _AutoNumber = autoNumber; _Validation = validation; _KeyInvalidate = keyInvalidate; } public async Task GetAssessmentList(LoginDTO loginDTO) { try { return await _AssessmentDAL.GetAssessmentList(loginDTO); } catch (Exception) { throw; } } public async Task GetAssessment(int assessmentId, LoginDTO loginDTO) { try { var assessment = await _AssessmentDAL.GetAssessment(assessmentId, loginDTO); if (assessment == null) return JsonConvert.SerializeObject(null); if (assessment.AssessmentMode == (byte)AssessmentMode.Simple) { assessment.Questions = await _AssessmentDAL.GetSimpleQuestions(assessmentId, loginDTO); } else { assessment.Paper = await _AssessmentDAL.GetAssessmentPaper(assessmentId, loginDTO); if (assessment.Paper != null) { assessment.Paper.Sections = await _AssessmentDAL.GetPaperSections(assessment.Paper.AssessmentPaperId, loginDTO); if (assessment.Paper.Sections != null) { foreach (var section in assessment.Paper.Sections) section.Questions = await _AssessmentDAL.GetSectionQuestions(section.PaperSectionId, loginDTO); } } } return JsonConvert.SerializeObject(assessment); } catch (Exception) { throw; } } public async Task SaveAssessment(SessionAssessmentDTO dto, LoginDTO loginDTO) { if (dto == null) throw new ArgumentNullException(nameof(dto)); AutoNumberDTO? autoNumberDTO = null; try { if (dto.ProgrammeSessionId == 0) throw new ValidationException("ProgrammeSessionId is required."); if (dto.PassThresholdPct < 0 || dto.PassThresholdPct > 100) throw new ValidationException("PassMarkPct must be between 0 and 100."); if (dto.MaxAttempts <= 0) throw new ValidationException("MaxAttempts must be greater than 0."); if (!Enum.IsDefined(typeof(AssessmentMode), dto.AssessmentMode)) throw new ValidationException($"Invalid AssessmentMode value: {dto.AssessmentMode}."); dto.TenantId = loginDTO.ClientId; dto.ModifiedById = loginDTO.UserId; dto.ModifiedOn = DateTime.UtcNow; string message; if (dto.AssessmentId == 0) { autoNumberDTO = await _AutoNumber.GetAutoNumber(1, "SESSIONASSESSMENT", loginDTO); dto.AssessmentId = autoNumberDTO.StartNumber; dto.CreatedById = loginDTO.UserId; dto.CreatedOn = DateTime.UtcNow; await _AssessmentDAL.SaveAssessment(dto, loginDTO); if (dto.AssessmentMode == (byte)AssessmentMode.Simple && dto.Questions != null) await SaveQuestions(dto.AssessmentId, dto.Questions, loginDTO); message = string.Format(SuccessResponse.SaveSuccessWithAssessmentTitle, dto.AssessmentTitle); } else { await _AssessmentDAL.UpdateAssessment(dto, loginDTO); if (dto.AssessmentMode == (byte)AssessmentMode.Simple) { // Delete in FK-safe order: responses → attempts → questions await _AssessmentDAL.DeleteAttemptResponsesByAssessment(dto.AssessmentId, loginDTO); await _AssessmentDAL.DeleteAttemptsByAssessment(dto.AssessmentId, loginDTO); await _AssessmentDAL.DeleteQuestions(dto.AssessmentId, loginDTO); if (dto.Questions != null) await SaveQuestions(dto.AssessmentId, dto.Questions, loginDTO); } message = string.Format(SuccessResponse.UpdateSuccessWithAssessmentTitle, dto.AssessmentTitle); } var cacheKey = new CacheKeyGeneration().KeyGeneration(dto.AssessmentId, EntityConstant.OBJECTSESSIONASSESSMENT, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return message; } catch (ValidationException vex) { throw new Exception(vex.Message); } catch (Exception) { if (autoNumberDTO != null && dto.AssessmentId > 0) await _AutoNumber.RollbackAutoNumber("SESSIONASSESSMENT", dto.AssessmentId, loginDTO); throw; } } public async Task GetSelectList(CriteriaDTO criteriaDTO, LoginDTO login, CancellationToken ct) => await _AssessmentDAL.GetSelectList(criteriaDTO, login, ct); public async Task DeleteAssessment(int assessmentId, LoginDTO loginDTO) { try { int result = await _AssessmentDAL.DeleteAssessment(assessmentId, loginDTO); if (result > 0) { var cacheKey = new CacheKeyGeneration().KeyGeneration(assessmentId, EntityConstant.OBJECTSESSIONASSESSMENT, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.DeleteSuccessMessage; } return ErrorResponse.DeleteNotFoundMessage; } catch (Exception) { throw; } } //public async Task SubmitAttempt(SubmitAttemptDTO dto, LoginDTO loginDTO) //{ // if (dto == null) throw new ArgumentNullException(nameof(dto)); // AutoNumberDTO? autoNumberDTO = null; // try // { // var assessment = await _AssessmentDAL.GetAssessment(dto.AssessmentId, loginDTO); // if (assessment == null) // throw new Exception("Assessment not found."); // int attemptCount = await _AssessmentDAL.GetAttemptCount(dto.AssessmentId, dto.EnrollmentId, loginDTO); // if (attemptCount >= assessment.MaxAttempts) // throw new Exception($"Maximum attempts ({assessment.MaxAttempts}) reached for this assessment."); // List questions; // if (assessment.AssessmentMode == (byte)AssessmentMode.Simple) // questions = await _AssessmentDAL.GetSimpleQuestions(dto.AssessmentId, loginDTO); // else // questions = new List(); // int correctAnswers = 0; // int totalQuestions = dto.Responses.Count; // var autoResponseNumber = await _AutoNumber.GetAutoNumber(dto.Responses.Count, "ATTEMPTRESPONSE", loginDTO); // int nextResponseId = autoResponseNumber.StartNumber; // foreach (var response in dto.Responses) // { // var question = questions.FirstOrDefault(q => q.AssessmentQuestionId == response.QuestionId); // response.IsCorrect = question != null && // string.Equals(response.SelectedAnswer, question.CorrectAnswer, StringComparison.OrdinalIgnoreCase); // if (response.IsCorrect) correctAnswers++; // response.ResponseId = nextResponseId++; // response.TenantId = loginDTO.ClientId; // } // decimal scorePct = totalQuestions > 0 ? Math.Round((decimal)correctAnswers / totalQuestions * 100, 2) : 0; // bool isPassed = scorePct >= assessment.PassThresholdPct; // autoNumberDTO = await _AutoNumber.GetAutoNumber(1, "ASSESSMENTATTEMPT", loginDTO); // var attempt = new AssessmentAttemptDTO // { // AttemptId = autoNumberDTO.StartNumber, // AssessmentId = dto.AssessmentId, // EnrollmentId = dto.EnrollmentId, // EmployeeId = dto.EmployeeId, // AttemptNo = attemptCount + 1, // StartedOn = DateTime.UtcNow, // SubmittedOn = DateTime.UtcNow, // ScorePct = scorePct, // AttemptIsPassed = isPassed, // TenantId = loginDTO.ClientId, // Responses = dto.Responses // }; // await _AssessmentDAL.SaveAttemptWithResponses(attempt, loginDTO); // return JsonConvert.SerializeObject(new { attempt.AttemptId, ScorePct = scorePct, IsPassed = isPassed }); // } // catch (Exception) // { // if (autoNumberDTO != null) // await _AutoNumber.RollbackAutoNumber("ASSESSMENTATTEMPT", autoNumberDTO.StartNumber, loginDTO); // throw; // } //} private async Task SaveQuestions(int assessmentId, List questions, LoginDTO loginDTO) { foreach (var question in questions) { question.AssessmentId = assessmentId; question.TenantId = loginDTO.ClientId; if (question.AssessmentQuestionId == 0) { var qAuto = await _AutoNumber.GetAutoNumber(1, "ASSESSMENTQUESTION", loginDTO); question.AssessmentQuestionId = qAuto.StartNumber; } await _AssessmentDAL.SaveQuestion(question, loginDTO); } } } }