using GermanApp.Domain.Interfaces;
namespace GermanApp.Application.Services;
///
/// Domain service for managing lesson unlocking business logic.
/// This is part of the Application layer.
///
public class LessonUnlockService
{
private readonly ILessonRepository _lessonRepository;
private readonly IUserProgressRepository _userProgressRepository;
public LessonUnlockService(
ILessonRepository lessonRepository,
IUserProgressRepository userProgressRepository)
{
_lessonRepository = lessonRepository;
_userProgressRepository = userProgressRepository;
}
///
/// Checks if the next lesson in a level is unlocked for the user.
/// A lesson is unlocked if:
/// - It's the first lesson in the level, OR
/// - The previous lesson in the level has been completed
///
/// The user ID
/// The current lesson ID
/// Cancellation token
/// True if the next lesson is unlocked, false otherwise
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;
}
///
/// Gets the next lesson ID that should be unlocked for a user after completing a lesson.
///
/// The user ID
/// The ID of the lesson that was just completed
/// Cancellation token
/// The ID of the next lesson, or null if no more lessons in the level
public virtual async Task GetNextLessonIdAsync(
int userId,
int completedLessonId,
CancellationToken cancellationToken = default)
{
var currentLesson = await _lessonRepository.GetByIdAsync(completedLessonId, cancellationToken);
if (currentLesson == null)
return null;
var nextLesson = await _lessonRepository.GetNextLessonAsync(completedLessonId, cancellationToken);
return nextLesson?.Id;
}
///
/// Gets all lessons that are accessible to a user (completed lessons + next unlocked lesson).
///
/// The user ID
/// Cancellation token
/// List of accessible lesson IDs
public virtual async Task> GetAccessibleLessonIdsAsync(
int userId,
CancellationToken cancellationToken = default)
{
var lessons = await _lessonRepository.GetAccessibleLessonsAsync(userId, cancellationToken);
return lessons.Select(l => l.Id).ToList();
}
///
/// Checks if a specific lesson is accessible to a user.
///
/// The user ID
/// The lesson ID to check
/// Cancellation token
/// True if the lesson is accessible, false otherwise
public virtual async Task IsLessonAccessibleAsync(
int userId,
int lessonId,
CancellationToken cancellationToken = default)
{
var accessibleLessons = await GetAccessibleLessonIdsAsync(userId, cancellationToken);
return accessibleLessons.Contains(lessonId);
}
}