using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using LanguageExt; using MediatR; using static ErsatzTV.Application.Channels.Mapper; namespace ErsatzTV.Application.Channels.Commands { public class CreateChannelHandler : IRequestHandler> { private readonly IChannelRepository _channelRepository; private readonly IFFmpegProfileRepository _ffmpegProfileRepository; public CreateChannelHandler( IChannelRepository channelRepository, IFFmpegProfileRepository ffmpegProfileRepository) { _channelRepository = channelRepository; _ffmpegProfileRepository = ffmpegProfileRepository; } public Task> Handle( CreateChannel request, CancellationToken cancellationToken) => Validate(request) .MapT(PersistChannel) .Bind(v => v.ToEitherAsync()); private Task PersistChannel(Channel c) => _channelRepository.Add(c).Map(ProjectToViewModel); private async Task> Validate(CreateChannel request) => (ValidateName(request), await ValidateNumber(request), await FFmpegProfileMustExist(request)) .Apply( (name, number, ffmpegProfileId) => { var artwork = new List(); if (!string.IsNullOrWhiteSpace(request.Logo)) { artwork.Add( new Artwork { Path = request.Logo, ArtworkKind = ArtworkKind.Logo, DateAdded = DateTime.UtcNow, DateUpdated = DateTime.UtcNow }); } return new Channel(Guid.NewGuid()) { Name = name, Number = number, FFmpegProfileId = ffmpegProfileId, StreamingMode = request.StreamingMode, Artwork = artwork }; }); private Validation ValidateName(CreateChannel createChannel) => createChannel.NotEmpty(c => c.Name) .Bind(_ => createChannel.NotLongerThan(50)(c => c.Name)); private async Task> ValidateNumber(CreateChannel createChannel) { Option maybeExistingChannel = await _channelRepository.GetByNumber(createChannel.Number); return maybeExistingChannel.Match>( _ => BaseError.New("Channel number must be unique"), () => { if (Regex.IsMatch(createChannel.Number, @"^[0-9]+(\.[0-9])?$")) { return createChannel.Number; } return BaseError.New("Invalid channel number; one decimal is allowed for subchannels"); }); } private async Task> FFmpegProfileMustExist(CreateChannel createChannel) => (await _ffmpegProfileRepository.Get(createChannel.FFmpegProfileId)) .ToValidation($"FFmpegProfile {createChannel.FFmpegProfileId} does not exist.") .Map(c => c.Id); } }