using GermanApp.Domain.Entities; using GermanApp.Domain.Interfaces; using GermanApp.Infrastructure.Data.DbContext; using Microsoft.EntityFrameworkCore; namespace GermanApp.Infrastructure.Data.Repositories; /// /// Entity Framework Core implementation of ILessonRepository. /// This is part of the Infrastructure layer. /// public class LessonRepository : ILessonRepository { private readonly AppDbContext _context; public LessonRepository(AppDbContext context) { _context = context; } public async Task GetByIdAsync(int id, CancellationToken cancellationToken = default) { return await _context.Lessons .Include(l => l.Level) .FirstOrDefaultAsync(l => l.Id == id, cancellationToken); } public async Task> GetAllAsync(CancellationToken cancellationToken = default) { return await _context.Lessons .Include(l => l.Level) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task AddAsync(Lesson entity, CancellationToken cancellationToken = default) { await _context.Lessons.AddAsync(entity, cancellationToken); await _context.SaveChangesAsync(cancellationToken); return entity; } public async Task UpdateAsync(Lesson entity, CancellationToken cancellationToken = default) { _context.Lessons.Update(entity); await _context.SaveChangesAsync(cancellationToken); } public async Task DeleteAsync(Lesson entity, CancellationToken cancellationToken = default) { _context.Lessons.Remove(entity); await _context.SaveChangesAsync(cancellationToken); } public async Task ExistsAsync(int id, CancellationToken cancellationToken = default) { return await _context.Lessons .AnyAsync(l => l.Id == id, cancellationToken); } public async Task> GetByLevelAsync(int levelId, CancellationToken cancellationToken = default) { return await _context.Lessons .Include(l => l.Level) .Where(l => l.LevelId == levelId) .OrderBy(l => l.Order) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task> GetAllOrderedAsync(CancellationToken cancellationToken = default) { return await _context.Lessons .Include(l => l.Level) .OrderBy(l => l.Level.Order) .ThenBy(l => l.Order) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task GetFirstLessonInLevelAsync(int levelId, CancellationToken cancellationToken = default) { return await _context.Lessons .Include(l => l.Level) .Where(l => l.LevelId == levelId) .OrderBy(l => l.Order) .FirstOrDefaultAsync(cancellationToken); } public async Task GetNextLessonAsync(int currentLessonId, CancellationToken cancellationToken = default) { var currentLesson = await _context.Lessons .Where(l => l.Id == currentLessonId) .Select(l => new { l.LevelId, l.Order }) .FirstOrDefaultAsync(cancellationToken); if (currentLesson == null) return null; return await _context.Lessons .Include(l => l.Level) .Where(l => l.LevelId == currentLesson.LevelId && l.Order > currentLesson.Order) .OrderBy(l => l.Order) .FirstOrDefaultAsync(cancellationToken); } public async Task> GetAccessibleLessonsAsync(int userId, CancellationToken cancellationToken = default) { // Get all lessons ordered by level and lesson order var allLessons = await _context.Lessons .Include(l => l.Level) .OrderBy(l => l.Level.Order) .ThenBy(l => l.Order) .AsNoTracking() .ToListAsync(cancellationToken); if (allLessons.Count == 0) return new List(); // Get completed lessons for this user var completedLessonIds = await _context.UserProgress .Where(up => up.UserId == userId && up.IsCompleted) .Select(up => up.LessonId) .ToListAsync(cancellationToken); // Find the first lesson in the first level var firstLesson = allLessons.First(); // If user hasn't completed any lessons, they can only access the first one if (completedLessonIds.Count == 0) return new List { firstLesson }; // Get the highest ordered lesson that the user has completed var completedLessons = allLessons .Where(l => completedLessonIds.Contains(l.Id)) .OrderBy(l => l.Level.Order) .ThenBy(l => l.Order) .ToList(); if (completedLessons.Count == 0) return new List { firstLesson }; var lastCompleted = completedLessons.Last(); // User can access all lessons up to and including the next one after the last completed var accessibleLessons = allLessons .TakeWhile(l => l.Id != lastCompleted.Id) .ToList(); // Add the last completed and the next one accessibleLessons.Add(lastCompleted); var nextLessonIndex = allLessons.FindIndex(l => l.Id == lastCompleted.Id) + 1; if (nextLessonIndex < allLessons.Count) { accessibleLessons.Add(allLessons[nextLessonIndex]); } return accessibleLessons; } }