using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using GB5Shared.Validation; using Newtonsoft.Json; using TMSDAL.DTO.AssessmentAttempt; using TMSDAL.DTO.AttemptResponse; // FIX 1: Add this using using TMSDAL.Query.AssessmentAttempt; using TMSDAL.Query.AttemptResponse; namespace TMSDAL.CustomCode.AssessmentAttempt { public class AssessmentAttemptDAL : IAssessmentAttemptDAL { private readonly IQueryExecutor _QueryExecutor; private readonly IValidation _Validation; public AssessmentAttemptDAL(IQueryExecutor queryExecutor, IValidation validation) { _QueryExecutor = queryExecutor; _Validation = validation; } // ── GET SINGLE (with child responses) ──────────────── public async Task GetAssessmentAttempt(int assessmentAttemptId, LoginDTO loginDTO) { try { var attempt = await _QueryExecutor.QuerySingleAsync( loginDTO, AssessmentAttemptQB.GET_ASSESSMENTATTEMPT, new { AssessmentAttemptId = assessmentAttemptId, TenantId = loginDTO.ClientId }); if (attempt == null) return JsonConvert.SerializeObject(null); attempt.Responses = (await _QueryExecutor.QueryAsync( loginDTO, AttemptResponseQB.GET_ATTEMPTRESPONSE_LIST, new { AssessmentAttemptId = assessmentAttemptId })).ToList(); return JsonConvert.SerializeObject(attempt); } catch (Exception) { throw; } } // ── GET FULL LIST (all attempts, with enrollment details) ── public async Task GetAssessmentAttemptFullList(LoginDTO loginDTO) { try { var result = await _QueryExecutor.QueryAsync( loginDTO, AssessmentAttemptQB.GET_ASSESSMENTATTEMPT_FULL_LIST, new { TenantId = loginDTO.ClientId }); return JsonConvert.SerializeObject(result); } catch (Exception) { throw; } } // ── GET LIST BY ENROLLMENT (with child responses) ───── public async Task GetAssessmentAttemptList(int enrollmentId, LoginDTO loginDTO) { try { var attempts = (await _QueryExecutor.QueryAsync( loginDTO, AssessmentAttemptQB.GET_ASSESSMENTATTEMPT_LIST, new { EnrollmentId = enrollmentId, TenantId = loginDTO.ClientId })).ToList(); if (!attempts.Any()) return JsonConvert.SerializeObject(attempts); // Fetch all responses in one shot using IN @AttemptIds var attemptIds = attempts.Select(a => a.AssessmentAttemptId).ToList(); var responses = (await _QueryExecutor.QueryAsync( loginDTO, AttemptResponseQB.GET_ATTEMPTRESPONSE_LIST_BY_ATTEMPT_IDS, new { AssessmentAttemptIds = attemptIds })).ToList(); // FIX 2: Explicitly type the dictionary so GetValueOrDefault can infer correctly var responseMap = responses .GroupBy(r => r.AssessmentAttemptId) .ToDictionary(g => g.Key, g => g.ToList()); foreach (var attempt in attempts) attempt.Responses = responseMap.TryGetValue(attempt.AssessmentAttemptId, out var list) ? list : null; return JsonConvert.SerializeObject(attempts); } catch (Exception) { throw; } } // ── GET SELECTLIST ──────────────────────────────────── public async Task GetSelectListAssessmentAttempt(CriteriaDTO criteriaDTO, LoginDTO loginDTO) { var result = await _QueryExecutor.QueryWithCriteriaAsync( loginDTO, AssessmentAttemptQB.GET_SELECTLIST_ASSESSMENTATTEMPT, new { TenantId = loginDTO.ClientId }, criteriaDTO); return JsonConvert.SerializeObject(result); } // ── GET ATTEMPT COUNT ───────────────────────────────── public async Task GetAttemptCount(int assessmentId, int enrollmentId, LoginDTO loginDTO) { try { return await _QueryExecutor.ExecuteScalarAsync( loginDTO, AssessmentAttemptQB.GET_ATTEMPT_COUNT, new { AssessmentId = assessmentId, EnrollmentId = enrollmentId, TenantId = loginDTO.ClientId }); } catch (Exception) { throw; } } // ── SAVE ────────────────────────────────────────────── public async Task SaveAssessmentAttempt(AssessmentAttemptDTO dto, LoginDTO loginDTO) { try { var param = new { dto.AssessmentAttemptId, dto.AssessmentId, dto.EnrollmentId, dto.AttemptNo, dto.StartedOn, dto.CompletedOn, dto.TotalMarks, dto.MarksObtained, dto.ScorePct, dto.AttemptIsPassed, dto.EvaluatedById, dto.IsLmsSynced, dto.Remarks, dto.DeliveryChannel, dto.Version, dto.Status, dto.SortOrder, dto.SourceType, dto.TenantId, dto.CreatedById, dto.CreatedOn, dto.ModifiedById, dto.ModifiedOn }; return await _QueryExecutor.ExecuteAsync( loginDTO, AssessmentAttemptQB.SAVE_ASSESSMENTATTEMPT, param); } catch (Exception ex) { string error = await _Validation.HandleException(ex, ErrorResponse.SaveErrorMessage); throw new Exception(error); } } // ── UPDATE SCORE ────────────────────────────────────── public async Task UpdateAssessmentAttemptScore(AssessmentAttemptDTO dto, LoginDTO loginDTO) { try { return await _QueryExecutor.ExecuteAsync( loginDTO, AssessmentAttemptQB.UPDATE_ASSESSMENTATTEMPT_SCORE, dto); } catch (Exception ex) { string error = await _Validation.HandleException(ex, ErrorResponse.UpdateErrorMessage); throw new Exception(error); } } // ── DELETE ──────────────────────────────────────────── public async Task DeleteAssessmentAttempt(int assessmentAttemptId, LoginDTO loginDTO) { try { // Delete child responses first (FK constraint) await _QueryExecutor.ExecuteAsync(loginDTO, AttemptResponseQB.DELETE_ATTEMPTRESPONSES_BY_ATTEMPT, new { AssessmentAttemptId = assessmentAttemptId }); int rows = await _QueryExecutor.ExecuteAsync(loginDTO, AssessmentAttemptQB.DELETE_ASSESSMENTATTEMPT, new { AssessmentAttemptId = assessmentAttemptId, TenantId = loginDTO.ClientId }); return rows > 0 ? SuccessResponse.DeleteSuccessMessage : ErrorResponse.DeleteNotFoundMessage; } catch (Exception ex) { string error = await _Validation.HandleException(ex, ErrorResponse.DeleteErrorMessage); throw new Exception(error); } } } }