using Microsoft.EntityFrameworkCore; using UniVerse.Application.DTOs.Common; using UniVerse.Application.DTOs.Courses; using UniVerse.Application.Interfaces; using UniVerse.Application.Mappings; using UniVerse.Domain.Entities; using UniVerse.Domain.Exceptions; using UniVerse.Infrastructure.Data; namespace UniVerse.Infrastructure.Services; public class CourseService : ICourseService { private readonly AppDbContext _db; public CourseService(AppDbContext db) => _db = db; public async Task> GetAllAsync(CourseFilterRequest filter) { var query = _db.Courses.Include(c => c.CourseTags).ThenInclude(ct => ct.Tag).AsQueryable(); if (!string.IsNullOrEmpty(filter.Search)) query = query.Where(c => c.Name.ToLower().Contains(filter.Search.ToLower())); if (filter.IsSynced.HasValue) query = query.Where(c => c.IsSynced == filter.IsSynced.Value); if (filter.TagId.HasValue) query = query.Where(c => c.CourseTags.Any(ct => ct.TagId == filter.TagId.Value)); var total = await query.CountAsync(); var courses = await query.OrderByDescending(c => c.CreatedAt) .Skip((filter.Page - 1) * filter.PageSize).Take(filter.PageSize).ToListAsync(); return PagedResult.Create(courses.Select(c => c.ToDto()).ToList(), total, filter.Page, filter.PageSize); } public async Task GetByIdAsync(int id) { var course = await _db.Courses.Include(c => c.CourseTags).ThenInclude(ct => ct.Tag) .FirstOrDefaultAsync(c => c.Id == id) ?? throw new NotFoundException("Course", id); return course.ToDto(); } public async Task CreateAsync(CreateCourseRequest request) { var course = new Course { Name = request.Name, Description = request.Description }; _db.Courses.Add(course); await _db.SaveChangesAsync(); return await GetByIdAsync(course.Id); } public async Task UpdateAsync(int id, UpdateCourseRequest request) { var course = await _db.Courses.FindAsync(id) ?? throw new NotFoundException("Course", id); course.Name = request.Name; course.Description = request.Description; course.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(); return await GetByIdAsync(id); } public async Task DeleteAsync(int id) { var course = await _db.Courses.FindAsync(id) ?? throw new NotFoundException("Course", id); _db.Courses.Remove(course); await _db.SaveChangesAsync(); } public async Task AddTagAsync(int courseId, int tagId) { if (await _db.CourseTags.AnyAsync(ct => ct.CourseId == courseId && ct.TagId == tagId)) throw new ConflictException("Tag already linked to course."); _ = await _db.Courses.FindAsync(courseId) ?? throw new NotFoundException("Course", courseId); _ = await _db.Tags.FindAsync(tagId) ?? throw new NotFoundException("Tag", tagId); _db.CourseTags.Add(new CourseTag { CourseId = courseId, TagId = tagId }); await _db.SaveChangesAsync(); } public async Task RemoveTagAsync(int courseId, int tagId) { var ct = await _db.CourseTags.FirstOrDefaultAsync(x => x.CourseId == courseId && x.TagId == tagId) ?? throw new NotFoundException("CourseTag not found."); _db.CourseTags.Remove(ct); await _db.SaveChangesAsync(); } }