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