using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using static ErsatzTV.Application.ProgramSchedules.Mapper; namespace ErsatzTV.Application.ProgramSchedules; public class CopyProgramScheduleHandler : IRequestHandler> { private readonly IDbContextFactory _dbContextFactory; public CopyProgramScheduleHandler(IDbContextFactory dbContextFactory) => _dbContextFactory = dbContextFactory; public async Task> Handle( CopyProgramSchedule request, CancellationToken cancellationToken) { try { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation validation = await Validate(dbContext, request); return await validation.Apply(p => PerformCopy(dbContext, p, request, cancellationToken)); } catch (Exception ex) { return BaseError.New(ex.Message); } } private async Task PerformCopy( TvContext dbContext, ProgramSchedule schedule, CopyProgramSchedule request, CancellationToken cancellationToken) { var clone = new ProgramSchedule(); await dbContext.AddAsync(clone, cancellationToken); clone.Name = request.Name; clone.RandomStartPoint = schedule.RandomStartPoint; clone.ShuffleScheduleItems = schedule.ShuffleScheduleItems; clone.TreatCollectionsAsShows = schedule.TreatCollectionsAsShows; clone.KeepMultiPartEpisodesTogether = schedule.KeepMultiPartEpisodesTogether; // no playouts, no alternates clone.Playouts = new List(); clone.ProgramScheduleAlternates = new List(); // clone all items clone.Items = new List(); foreach (ProgramScheduleItem item in schedule.Items) { PropertyValues itemValues = dbContext.Entry(item).CurrentValues.Clone(); itemValues["Id"] = 0; ProgramScheduleItem itemClone = item switch { ProgramScheduleItemFlood => new ProgramScheduleItemFlood(), ProgramScheduleItemDuration => new ProgramScheduleItemDuration(), ProgramScheduleItemMultiple => new ProgramScheduleItemMultiple(), _ => new ProgramScheduleItemOne() }; await dbContext.AddAsync(itemClone, cancellationToken); dbContext.Entry(itemClone).CurrentValues.SetValues(itemValues); itemClone.ProgramScheduleId = 0; itemClone.ProgramSchedule = clone; } await dbContext.SaveChangesAsync(cancellationToken); return ProjectToViewModel(clone); } private static async Task> Validate( TvContext dbContext, CopyProgramSchedule request) => (await ScheduleMustExist(dbContext, request), await ValidateName(dbContext, request)) .Apply((programSchedule, _) => programSchedule); private static Task> ScheduleMustExist( TvContext dbContext, CopyProgramSchedule request) => dbContext.ProgramSchedules .AsNoTracking() .Include(ps => ps.Items) .SelectOneAsync(p => p.Id, p => p.Id == request.ProgramScheduleId) .Map(o => o.ToValidation("Schedule does not exist.")); private static async Task> ValidateName( TvContext dbContext, CopyProgramSchedule request) { List allNames = await dbContext.ProgramSchedules .Map(ps => ps.Name) .ToListAsync(); Validation result1 = request.NotEmpty(c => c.Name) .Bind(_ => request.NotLongerThan(50)(c => c.Name)); var result2 = Optional(request.Name) .Where(name => !allNames.Contains(name)) .ToValidation("Schedule name must be unique"); return (result1, result2).Apply((_, _) => request.Name); } }