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