using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; 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 IStoryRepository. /// This is part of the Infrastructure layer. /// public class StoryRepository : IStoryRepository { private readonly AppDbContext _context; public StoryRepository(AppDbContext context) { _context = context; } public async Task GetByIdAsync(int id, CancellationToken cancellationToken = default) { return await _context.StorySegments .Include(s => s.Level) .Include(s => s.Lesson) .FirstOrDefaultAsync(s => s.Id == id, cancellationToken); } public async Task> GetByLevelAsync( int levelId, bool includeInactive = false, CancellationToken cancellationToken = default) { var query = _context.StorySegments .Where(s => s.LevelId == levelId); if (!includeInactive) { query = query.Where(s => s.IsActive); } return await query .Include(s => s.Level) .Include(s => s.Lesson) .OrderBy(s => s.Order) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task> GetByLessonAsync( int lessonId, CancellationToken cancellationToken = default) { return await _context.StorySegments .Where(s => s.LessonId == lessonId) .Include(s => s.Level) .Include(s => s.Lesson) .OrderBy(s => s.Order) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task GetNextSegmentToUnlockAsync( int levelId, int completedLessonOrder, CancellationToken cancellationToken = default) { // Get the next lesson order (the one after the completed lesson) // Then find the story segment with matching Lesson.Order var nextLessonOrder = completedLessonOrder + 1; return await _context.StorySegments .Where(s => s.LevelId == levelId && s.LessonId != null && s.IsActive) .Where(s => s.Lesson != null && s.Lesson.Order == nextLessonOrder) .Include(s => s.Lesson) .OrderBy(s => s.Order) .FirstOrDefaultAsync(cancellationToken); } public async Task> GetByOrderRangeAsync( int levelId, int startOrder, int endOrder, CancellationToken cancellationToken = default) { return await _context.StorySegments .Where(s => s.LevelId == levelId && s.Order >= startOrder && s.Order <= endOrder && s.IsActive) .Include(s => s.Level) .Include(s => s.Lesson) .OrderBy(s => s.Order) .AsNoTracking() .ToListAsync(cancellationToken); } public async Task AddAsync(StorySegment segment, CancellationToken cancellationToken = default) { await _context.StorySegments.AddAsync(segment, cancellationToken); await _context.SaveChangesAsync(cancellationToken); return segment; } public async Task UpdateAsync(StorySegment segment, CancellationToken cancellationToken = default) { _context.StorySegments.Update(segment); await _context.SaveChangesAsync(cancellationToken); } public async Task DeleteAsync(int id, CancellationToken cancellationToken = default) { var segment = await _context.StorySegments.FindAsync(new object[] { id }, cancellationToken); if (segment != null) { _context.StorySegments.Remove(segment); await _context.SaveChangesAsync(cancellationToken); } } public async Task GetMaxOrderAsync(int levelId, CancellationToken cancellationToken = default) { var maxOrder = await _context.StorySegments .Where(s => s.LevelId == levelId) .Select(s => s.Order) .MaxAsync(cancellationToken); return maxOrder ?? 0; } public async Task ExistsAsync(int id, CancellationToken cancellationToken = default) { return await _context.StorySegments .AnyAsync(s => s.Id == id, cancellationToken); } public async Task> GetSegmentsNeedingAudioAsync( CancellationToken cancellationToken = default) { return await _context.StorySegments .Where(s => string.IsNullOrEmpty(s.AudioUrl) && s.IsActive) .Include(s => s.Level) .Include(s => s.Lesson) .OrderBy(s => s.LevelId) .ThenBy(s => s.Order) .AsNoTracking() .ToListAsync(cancellationToken); } }