using AutoMapper; using GameIdeas.Shared.Constants; using GameIdeas.Shared.Dto; using GameIdeas.Shared.Exceptions; using GameIdeas.Shared.Model; using GameIdeas.WebAPI.Context; using GameIdeas.WebAPI.Services.Categories; using Microsoft.EntityFrameworkCore; using System.Linq.Expressions; namespace GameIdeas.WebAPI.Services.Games; public class GameReadService(GameIdeasContext context, IMapper mapper, ICategoryService categoryService) : IGameReadService { public async Task> GetGames(GameFilterDto filter) { var query = context.Games .Include(g => g.GamePlatforms).ThenInclude(gp => gp.Platform) .Include(g => g.GameProperties) .Include(g => g.GameTags).ThenInclude(gt => gt.Tag) .Include(g => g.Publisher) .Include(g => g.Developer) .AsQueryable(); ApplyFilter(ref query, filter); ApplyOrder(ref query, filter); var games = await query.Skip((filter.CurrentPage - 1) * GlobalConstants.NUMBER_PER_PAGE) .Take(GlobalConstants.NUMBER_PER_PAGE) .ToListAsync(); ApplyStaticFilter(ref games, filter); return mapper.Map>(games); } public async Task GetGameById(int gameId) { var game = await context.Games .Include(g => g.CreationUser) .Include(g => g.ModificationUser) .Include(g => g.GamePlatforms).ThenInclude(p => p.Platform) .Include(g => g.GameProperties).ThenInclude(p => p.Property) .Include(g => g.GameTags).ThenInclude(p => p.Tag) .Include(g => g.Publisher) .Include(g => g.Developer) .FirstOrDefaultAsync(g => g.Id == gameId); return game == null ? throw new NotFoundException($"[{typeof(Game).FullName}] with ID {gameId} has not been found in context") : mapper.Map(game); } private static void ApplyOrder(ref IQueryable query, GameFilterDto filter) { if (filter.SortType != null && filter.SortPropertyName != null) { var param = Expression.Parameter(typeof(Game), "x"); Expression propertyAccess = Expression.PropertyOrField(param, filter.SortPropertyName); var converted = Expression.Convert(propertyAccess, typeof(object)); var lambda = Expression.Lambda>(converted, param); if (filter.SortType == Shared.Enum.SortType.Ascending) { query = Queryable.OrderBy(query, lambda); } else { query = Queryable.OrderByDescending(query, lambda); } } } private static void ApplyFilter(ref IQueryable query, GameFilterDto filter) { if (filter.PlatformIds != null) { query = query.Where(game => filter.PlatformIds.All(plat => game.GamePlatforms.Any(gp => gp.PlatformId == plat))); } if (filter.PropertyIds != null) { query = query.Where(game => filter.PropertyIds.All(prop => game.GameProperties.Any(gp => gp.PropertyId == prop))); } if (filter.TagIds != null) { query = query.Where(game => filter.TagIds.All(tag => game.GameTags.Any(gt => gt.TagId == tag))); } if (filter.PublisherIds != null) { query = query.Where(game => game.Publisher != null && filter.PublisherIds.Contains(game.Publisher!.Id)); } if (filter.DeveloperIds != null) { query = query.Where(game => game.Developer != null && filter.DeveloperIds.Contains(game.Developer.Id)); } if (filter.MinInterest != null) { query = query.Where(game => game.Interest >= filter.MinInterest); } if (filter.MaxInterest != null) { query = query.Where(game => game.Interest <= filter.MaxInterest); } if (filter.ReleaseYears != null) { query = query.Where(game => game.ReleaseDate != null && filter.ReleaseYears.Contains(game.ReleaseDate.Value.Year)); } } private void ApplyStaticFilter(ref List games, GameFilterDto filter) { if (!string.IsNullOrWhiteSpace(filter.Title)) { var keywords = filter.Title? .Split([' '], StringSplitOptions.RemoveEmptyEntries) .Select(k => k.Trim()) .ToArray() ?? []; games = games .Where(game => keywords.All( kw => game.Title.Contains(kw, StringComparison.OrdinalIgnoreCase) )) .OrderBy(game => keywords.Min(kw => game.Title.IndexOf(kw, StringComparison.OrdinalIgnoreCase) )) .ThenBy(game => game.Title.Length) .ToList(); return; } if (filter.StorageSpaces != null) { var storageSpaces = categoryService.GetStorageSpaces().Where(stor => filter.StorageSpaces.Contains(stor.Id)); games = games .Where(game => storageSpaces.Any(stor => (stor.MinSize ?? int.MinValue) <= game.StorageSpace && (stor.MaxSize ?? int.MaxValue) > game.StorageSpace)) .ToList(); } } }