using GermanApp.Application.DTOs; using GermanApp.Domain.Interfaces; namespace GermanApp.Application.Services; /// /// Service for calculating level completion metrics. /// This is part of the Application layer. /// public class LevelCompletionCalculator { private readonly ILevelRepository _levelRepository; private readonly ILessonRepository _lessonRepository; private readonly IUserProgressRepository _userProgressRepository; public LevelCompletionCalculator( ILevelRepository levelRepository, ILessonRepository lessonRepository, IUserProgressRepository userProgressRepository) { _levelRepository = levelRepository; _lessonRepository = lessonRepository; _userProgressRepository = userProgressRepository; } /// /// Calculates the completion percentage for a specific level for a user. /// /// The user ID /// The level ID /// Cancellation token /// Completion percentage (0-100) public virtual async Task CalculateLevelCompletionPercentageAsync( int userId, int levelId, CancellationToken cancellationToken = default) { // Get total lessons in the level var lessons = await _lessonRepository.GetByLevelAsync(levelId, cancellationToken); var totalLessons = lessons.Count; if (totalLessons == 0) return 0; // Get completed lessons count for user in this level var completedCount = await _userProgressRepository.GetLevelCompletionPercentageAsync( userId, levelId, cancellationToken); // The repository method returns percentage, but we need the count // Let's recalculate properly var completedLessons = 0; foreach (var lesson in lessons) { var hasCompleted = await _userProgressRepository.HasUserCompletedLessonAsync( userId, lesson.Id, cancellationToken); if (hasCompleted) completedLessons++; } return (double)completedLessons / totalLessons * 100; } /// /// Gets completion information for all levels for a user. /// /// The user ID /// Cancellation token /// List of level completion DTOs public virtual async Task> CalculateAllLevelCompletionsAsync( 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 totalLessons = lessons.Count; var completedCount = 0; foreach (var lesson in lessons) { var hasCompleted = await _userProgressRepository.HasUserCompletedLessonAsync( userId, lesson.Id, cancellationToken); if (hasCompleted) completedCount++; } var percentage = totalLessons > 0 ? (double)completedCount / totalLessons * 100 : 0; completions.Add(new LevelCompletionDto( level.Id, level.Name, level.Code, totalLessons, completedCount, percentage )); } return completions; } /// /// Checks if a user has completed all lessons in a level. /// /// The user ID /// The level ID /// Cancellation token /// True if all lessons are completed, false otherwise public virtual async Task IsLevelFullyCompletedAsync( int userId, int levelId, CancellationToken cancellationToken = default) { var percentage = await CalculateLevelCompletionPercentageAsync(userId, levelId, cancellationToken); return percentage >= 100; } /// /// Gets the number of completed lessons in a level for a user. /// /// The user ID /// The level ID /// Cancellation token /// Number of completed lessons public virtual async Task GetCompletedLessonCountAsync( int userId, int levelId, CancellationToken cancellationToken = default) { var lessons = await _lessonRepository.GetByLevelAsync(levelId, cancellationToken); var completedCount = 0; foreach (var lesson in lessons) { var hasCompleted = await _userProgressRepository.HasUserCompletedLessonAsync( userId, lesson.Id, cancellationToken); if (hasCompleted) completedCount++; } return completedCount; } /// /// Gets the number of total lessons in a level. /// /// The level ID /// Cancellation token /// Total number of lessons public virtual async Task GetTotalLessonCountAsync( int levelId, CancellationToken cancellationToken = default) { var lessons = await _lessonRepository.GetByLevelAsync(levelId, cancellationToken); return lessons.Count; } /// /// Gets the next level ID that should be unlocked for a user. /// Returns the next level if the current level is fully completed. /// /// The user ID /// The current level ID /// Cancellation token /// The next level ID, or null if no more levels public virtual async Task GetNextLevelIdAsync( int userId, int currentLevelId, CancellationToken cancellationToken = default) { var isCompleted = await IsLevelFullyCompletedAsync(userId, currentLevelId, cancellationToken); if (!isCompleted) return null; var nextLevel = await _levelRepository.GetNextLevelAsync(currentLevelId, cancellationToken); return nextLevel?.Id; } }