using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Search; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using static ErsatzTV.Application.Watermarks.Mapper; namespace ErsatzTV.Application.Watermarks; public class CopyWatermarkHandler(IDbContextFactory dbContextFactory, ISearchTargets searchTargets) : IRequestHandler> { public async Task> Handle( CopyWatermark request, CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(c => PerformCopy(dbContext, c, cancellationToken)); } private async Task PerformCopy( TvContext dbContext, CopyWatermarkParameters parameters, CancellationToken cancellationToken) { PropertyValues values = dbContext.Entry(parameters.Watermark).CurrentValues.Clone(); values["Id"] = 0; var clone = new ChannelWatermark(); await dbContext.AddAsync(clone, cancellationToken); dbContext.Entry(clone).CurrentValues.SetValues(values); clone.Name = parameters.Name; await dbContext.SaveChangesAsync(cancellationToken); searchTargets.SearchTargetsChanged(); return ProjectToViewModel(clone); } private static async Task> Validate( TvContext dbContext, CopyWatermark request, CancellationToken cancellationToken) => (await ValidateName(dbContext, request), await WatermarkMustExist(dbContext, request, cancellationToken)) .Apply((name, watermark) => new CopyWatermarkParameters(name, watermark)); private static async Task> ValidateName(TvContext dbContext, CopyWatermark request) { Validation result1 = request.NotEmpty(c => c.Name) .Bind(_ => request.NotLongerThan(50)(c => c.Name)); bool duplicateName = await dbContext.ChannelWatermarks .AnyAsync(wm => wm.Name == request.Name); Validation result2 = duplicateName ? Fail("ChannelWatermark name must be unique") : Success(Unit.Default); return (result1, result2).Apply((_, _) => request.Name); } private static Task> WatermarkMustExist( TvContext dbContext, CopyWatermark copyWatermark, CancellationToken cancellationToken) => dbContext.ChannelWatermarks .SelectOneAsync(wm => wm.Id, wm => wm.Id == copyWatermark.WatermarkId, cancellationToken) .Map(o => o.ToValidation($"Watermark {copyWatermark.WatermarkId} does not exist.")); private sealed record CopyWatermarkParameters(string Name, ChannelWatermark Watermark); }