using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using ErsatzTV.Application.Playouts.Commands; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using LanguageExt; using Microsoft.EntityFrameworkCore; using static LanguageExt.Prelude; namespace ErsatzTV.Application.MediaCollections.Commands { public class AddItemsToCollectionHandler : MediatR.IRequestHandler> { private readonly ChannelWriter _channel; private readonly IDbContextFactory _dbContextFactory; private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMovieRepository _movieRepository; private readonly ITelevisionRepository _televisionRepository; public AddItemsToCollectionHandler( IDbContextFactory dbContextFactory, IMediaCollectionRepository mediaCollectionRepository, IMovieRepository movieRepository, ITelevisionRepository televisionRepository, ChannelWriter channel) { _dbContextFactory = dbContextFactory; _mediaCollectionRepository = mediaCollectionRepository; _movieRepository = movieRepository; _televisionRepository = televisionRepository; _channel = channel; } public async Task> Handle( AddItemsToCollection request, CancellationToken cancellationToken) { await using TvContext dbContext = _dbContextFactory.CreateDbContext(); Validation validation = await Validate(dbContext, request); return await validation.Apply(c => ApplyAddItemsRequest(dbContext, c, request)); } private async Task ApplyAddItemsRequest(TvContext dbContext, Collection collection, AddItemsToCollection request) { var allItems = request.MovieIds .Append(request.ShowIds) .Append(request.SeasonIds) .Append(request.EpisodeIds) .Append(request.ArtistIds) .Append(request.MusicVideoIds) .Append(request.OtherVideoIds) .Append(request.SongIds) .ToList(); var toAddIds = allItems.Where(item => collection.MediaItems.All(mi => mi.Id != item)).ToList(); List toAdd = await dbContext.MediaItems .Filter(mi => toAddIds.Contains(mi.Id)) .ToListAsync(); collection.MediaItems.AddRange(toAdd); if (await dbContext.SaveChangesAsync() > 0) { // rebuild all playouts that use this collection foreach (int playoutId in await _mediaCollectionRepository .PlayoutIdsUsingCollection(request.CollectionId)) { await _channel.WriteAsync(new BuildPlayout(playoutId, true)); } } return Unit.Default; } private async Task> Validate( TvContext dbContext, AddItemsToCollection request) => (await CollectionMustExist(dbContext, request), await ValidateMovies(request), await ValidateShows(request), await ValidateSeasons(request), await ValidateEpisodes(request)) .Apply((collection, _, _, _, _) => collection); private static Task> CollectionMustExist( TvContext dbContext, AddItemsToCollection request) => dbContext.Collections .Include(c => c.MediaItems) .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) .Map(o => o.ToValidation("Collection does not exist.")); private Task> ValidateMovies(AddItemsToCollection request) => _movieRepository.AllMoviesExist(request.MovieIds) .Map(Optional) .Filter(v => v == true) .MapT(_ => Unit.Default) .Map(v => v.ToValidation("Movie does not exist")); private Task> ValidateShows(AddItemsToCollection request) => _televisionRepository.AllShowsExist(request.ShowIds) .Map(Optional) .Filter(v => v == true) .MapT(_ => Unit.Default) .Map(v => v.ToValidation("Show does not exist")); private Task> ValidateSeasons(AddItemsToCollection request) => _televisionRepository.AllSeasonsExist(request.SeasonIds) .Map(Optional) .Filter(v => v == true) .MapT(_ => Unit.Default) .Map(v => v.ToValidation("Season does not exist")); private Task> ValidateEpisodes(AddItemsToCollection request) => _televisionRepository.AllEpisodesExist(request.EpisodeIds) .Map(Optional) .Filter(v => v == true) .MapT(_ => Unit.Default) .Map(v => v.ToValidation("Episode does not exist")); } }