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;
}
}