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