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