using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using Microsoft.EntityFrameworkCore; using static LanguageExt.Prelude; namespace ErsatzTV.Infrastructure.Data.Repositories { public class MediaCollectionRepository : IMediaCollectionRepository { private readonly TvContext _dbContext; public MediaCollectionRepository(TvContext dbContext) => _dbContext = dbContext; public async Task Add(SimpleMediaCollection collection) { await _dbContext.SimpleMediaCollections.AddAsync(collection); await _dbContext.SaveChangesAsync(); return collection; } public Task> Get(int id) => _dbContext.MediaCollections.SingleOrDefaultAsync(c => c.Id == id).Map(Optional); public Task> GetSimpleMediaCollection(int id) => _dbContext.SimpleMediaCollections .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); public Task> GetSimpleMediaCollectionWithItems(int id) => _dbContext.SimpleMediaCollections .Include(s => s.Movies) .ThenInclude(m => m.Source) .Include(s => s.TelevisionShows) .ThenInclude(s => s.Metadata) .Include(s => s.TelevisionSeasons) .Include(s => s.TelevisionEpisodes) .ThenInclude(s => s.Metadata) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); public Task> GetSimpleMediaCollectionWithItemsUntracked(int id) => _dbContext.SimpleMediaCollections .AsNoTracking() .Include(s => s.Movies) .ThenInclude(i => i.Source) .Include(s => s.Movies) .ThenInclude(m => m.Metadata) .Include(s => s.TelevisionShows) .ThenInclude(s => s.Metadata) .Include(s => s.TelevisionSeasons) .ThenInclude(s => s.TelevisionShow) .ThenInclude(s => s.Metadata) .Include(s => s.TelevisionEpisodes) .ThenInclude(s => s.Metadata) .Include(s => s.TelevisionEpisodes) .ThenInclude(e => e.Season) .ThenInclude(s => s.TelevisionShow) .ThenInclude(s => s.Metadata) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional); public Task> GetSimpleMediaCollections() => _dbContext.SimpleMediaCollections.ToListAsync(); public Task> GetAll() => _dbContext.MediaCollections.ToListAsync(); public Task>> GetItems(int id) => Get(id).MapT( collection => collection switch { SimpleMediaCollection s => SimpleItems(s), _ => throw new NotSupportedException($"Unsupported collection type {collection.GetType().Name}") }).Bind(x => x.Sequence()); public Task>> GetSimpleMediaCollectionItems(int id) => GetSimpleMediaCollection(id).MapT(SimpleItems).Bind(x => x.Sequence()); public Task Update(SimpleMediaCollection collection) { _dbContext.SimpleMediaCollections.Update(collection); return _dbContext.SaveChangesAsync(); } public async Task Delete(int mediaCollectionId) { MediaCollection mediaCollection = await _dbContext.MediaCollections.FindAsync(mediaCollectionId); _dbContext.MediaCollections.Remove(mediaCollection); await _dbContext.SaveChangesAsync(); } private async Task> SimpleItems(SimpleMediaCollection collection) { var result = new List(); await _dbContext.Entry(collection).Collection(c => c.Movies).LoadAsync(); result.AddRange(collection.Movies); result.AddRange(await GetTelevisionShowItems(collection)); result.AddRange(await GetTelevisionSeasonItems(collection)); result.AddRange(await GetTelevisionEpisodeItems(collection)); return result.Distinct().ToList(); } private async Task> GetTelevisionShowItems(SimpleMediaCollection collection) { // TODO: would be nice to get the media items in one go, but ef... List showItemIds = await _dbContext.GenericIntegerIds.FromSqlRaw( @"select tmi.Id from TelevisionEpisodes tmi inner join TelevisionSeasons tsn on tsn.Id = tmi.SeasonId inner join TelevisionShows ts on ts.Id = tsn.TelevisionShowId inner join SimpleMediaCollectionShows s on s.TelevisionShowsId = ts.Id where s.SimpleMediaCollectionsId = {0}", collection.Id) .Select(i => i.Id) .ToListAsync(); return await _dbContext.TelevisionEpisodeMediaItems .AsNoTracking() .Include(e => e.Metadata) .Where(mi => showItemIds.Contains(mi.Id)) .ToListAsync(); } private async Task> GetTelevisionSeasonItems(SimpleMediaCollection collection) { // TODO: would be nice to get the media items in one go, but ef... List seasonItemIds = await _dbContext.GenericIntegerIds.FromSqlRaw( @"select tmi.Id from TelevisionEpisodes tmi inner join TelevisionSeasons tsn on tsn.Id = tmi.SeasonId inner join SimpleMediaCollectionSeasons s on s.TelevisionSeasonsId = tsn.Id where s.SimpleMediaCollectionsId = {0}", collection.Id) .Select(i => i.Id) .ToListAsync(); return await _dbContext.TelevisionEpisodeMediaItems .AsNoTracking() .Include(e => e.Metadata) .Where(mi => seasonItemIds.Contains(mi.Id)) .ToListAsync(); } private async Task> GetTelevisionEpisodeItems(SimpleMediaCollection collection) { // TODO: would be nice to get the media items in one go, but ef... List episodeItemIds = await _dbContext.GenericIntegerIds.FromSqlRaw( @"select s.TelevisionEpisodesId as Id from SimpleMediaCollectionEpisodes s where s.SimpleMediaCollectionsId = {0}", collection.Id) .Select(i => i.Id) .ToListAsync(); return await _dbContext.TelevisionEpisodeMediaItems .AsNoTracking() .Include(e => e.Metadata) .Where(mi => episodeItemIds.Contains(mi.Id)) .ToListAsync(); } } }