using GermanApp.Application.DTOs; using GermanApp.Domain.Entities; using GermanApp.Domain.Interfaces; namespace GermanApp.Application.Services; /// /// Application service for managing user progress through lessons. /// This is part of the Application layer. /// public class ProgressService { private readonly IUserProgressRepository _userProgressRepository; private readonly ILessonRepository _lessonRepository; private readonly ILevelRepository _levelRepository; public ProgressService( IUserProgressRepository userProgressRepository, ILessonRepository lessonRepository, ILevelRepository levelRepository) { _userProgressRepository = userProgressRepository; _lessonRepository = lessonRepository; _levelRepository = levelRepository; } /// /// Gets user progress for a specific lesson. /// public virtual async Task GetUserProgressAsync(int userId, int lessonId, CancellationToken cancellationToken = default) { var progress = await _userProgressRepository.GetByUserAndLessonAsync(userId, lessonId, cancellationToken); return progress?.ToDto(); } /// /// Gets all progress records for a user. /// public virtual async Task> GetUserProgressAsync(int userId, CancellationToken cancellationToken = default) { var progressRecords = await _userProgressRepository.GetByUserAsync(userId, cancellationToken); return progressRecords.Select(p => p.ToDto()).ToList(); } /// /// Gets progress for all lessons in a specific level for a user. /// public virtual async Task> GetUserProgressByLevelAsync(int userId, int levelId, CancellationToken cancellationToken = default) { var progressRecords = await _userProgressRepository.GetByUserAndLevelAsync(userId, levelId, cancellationToken); return progressRecords.Select(p => p.ToDto()).ToList(); } /// /// Checks if a user has completed a lesson. /// public virtual async Task HasUserCompletedLessonAsync(int userId, int lessonId, CancellationToken cancellationToken = default) { return await _userProgressRepository.HasUserCompletedLessonAsync(userId, lessonId, cancellationToken); } /// /// Marks a lesson as completed for a user with a quiz score. /// public virtual async Task MarkLessonAsCompletedAsync(int userId, int lessonId, int quizScore, CancellationToken cancellationToken = default) { var progress = await _userProgressRepository.GetByUserAndLessonAsync(userId, lessonId, cancellationToken); if (progress == null) { // Create new progress record progress = UserProgress.Create(userId, lessonId); progress.MarkAsCompleted(quizScore); await _userProgressRepository.AddAsync(progress, cancellationToken); } else { // Update existing progress record progress.MarkAsCompleted(quizScore); await _userProgressRepository.UpdateAsync(progress, cancellationToken); } return progress.ToDto(); } /// /// Updates user progress for a lesson without marking as completed. /// public virtual async Task UpdateUserProgressAsync(int userId, UpdateUserProgressDto dto, CancellationToken cancellationToken = default) { var progress = await _userProgressRepository.GetByUserAndLessonAsync(userId, dto.LessonId, cancellationToken); if (progress == null) { // Create new progress record progress = dto.ToEntity(userId); await _userProgressRepository.AddAsync(progress, cancellationToken); } else { // Update existing progress record progress.UpdateFromDto(dto); await _userProgressRepository.UpdateAsync(progress, cancellationToken); } return progress.ToDto(); } /// /// Resets user progress for a lesson. /// public virtual async Task ResetLessonProgressAsync(int userId, int lessonId, CancellationToken cancellationToken = default) { var progress = await _userProgressRepository.GetByUserAndLessonAsync(userId, lessonId, cancellationToken); if (progress == null) return false; progress.Reset(); await _userProgressRepository.UpdateAsync(progress, cancellationToken); return true; } /// /// Gets the user's average score for a level. /// public virtual async Task GetAverageScoreForLevelAsync(int userId, int levelId, CancellationToken cancellationToken = default) { return await _userProgressRepository.GetAverageScoreForLevelAsync(userId, levelId, cancellationToken); } /// /// Gets the percentage of lessons completed in a level. /// public virtual async Task GetLevelCompletionPercentageAsync(int userId, int levelId, CancellationToken cancellationToken = default) { return await _userProgressRepository.GetLevelCompletionPercentageAsync(userId, levelId, cancellationToken); } /// /// Gets level completion summaries for a user. /// public virtual async Task> GetLevelCompletionsAsync(int userId, CancellationToken cancellationToken = default) { var levels = await _levelRepository.GetAllOrderedAsync(cancellationToken); var completions = new List(); foreach (var level in levels) { var lessons = await _lessonRepository.GetByLevelAsync(level.Id, cancellationToken); var completedCount = 0; foreach (var lesson in lessons) { var hasCompleted = await _userProgressRepository.HasUserCompletedLessonAsync(userId, lesson.Id, cancellationToken); if (hasCompleted) completedCount++; } var percentage = lessons.Count > 0 ? (double)completedCount / lessons.Count * 100 : 0; completions.Add(new LevelCompletionDto( level.Id, level.Name, level.Code, lessons.Count, completedCount, percentage )); } return completions; } /// /// Gets lessons that the user can access (completed previous lessons or first in level). /// public virtual async Task> GetAccessibleLessonsAsync(int userId, CancellationToken cancellationToken = default) { var lessons = await _lessonRepository.GetAccessibleLessonsAsync(userId, cancellationToken); return lessons.Select(l => l.ToDto()).ToList(); } /// /// Checks if the next lesson in a level is unlocked for the user. /// public virtual async Task IsNextLessonUnlockedAsync(int userId, int currentLessonId, CancellationToken cancellationToken = default) { var currentLesson = await _lessonRepository.GetByIdAsync(currentLessonId, cancellationToken); if (currentLesson == null) return false; // If this is the first lesson in the level, the next one is always unlocked var firstLesson = await _lessonRepository.GetFirstLessonInLevelAsync(currentLesson.LevelId, cancellationToken); if (firstLesson?.Id == currentLessonId) { var nextLesson = await _lessonRepository.GetNextLessonAsync(currentLessonId, cancellationToken); return nextLesson != null; } // Check if the user has completed the current lesson var hasCompleted = await _userProgressRepository.HasUserCompletedLessonAsync(userId, currentLessonId, cancellationToken); return hasCompleted; } }