using Ardalis.Result; using Mediator; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Musify.Application.Configuration; using Musify.Application.Common; using Musify.Application.Events; using Musify.Application.PlayLists.Commands; using Musify.Application.PlayLists.Responses; using Musify.Application.Extensions; using Musify.Application.Services; using Musify.Domain.Entities; using Musify.Domain.ValueObjects; using Musify.Application.Contracts; namespace Musify.Application.PlayLists.Handlers { public class UpdatePlayListCommandHandler( IEventBus eventBus, IDatabase database, UploadIntentValidator uploadIntentValidator, ILogger logger, ApplicationStorageConfiguration storageConfiguration, PlayListConfiguration playListConfiguration, UploadIntentConfiguration uploadIntentConfiguration) : ICommandHandler> { public async ValueTask> Handle(UpdatePlayListCommand request, CancellationToken cancellationToken) { var playListEntity = await database.PlayLists.SingleOrDefaultAsync(pl => pl.Id == request.PlayListId, cancellationToken); if (playListEntity is null) { logger.LogInformation("Playlist {PlayListId} not found", request.PlayListId); return Result.NotFound(); } if (playListEntity.UserId != request.UserId) { logger.LogWarning("User {UserId} is not the owner of playlist {PlayListId}", request.UserId, request.PlayListId); return Result.Unauthorized(); } if (request.NewName is not null) { playListEntity.Name = request.NewName; playListEntity.NormalizedName = request.NewName.Trim().ToUpperInvariant(); } if (request.NewDescription is not null) { playListEntity.Description = request.NewDescription; } UploadIntent? pictureIntent = null; string? finalPictureKey = null; if (request.NewPictureIntentId.HasValue) { var validation = await uploadIntentValidator.ValidateAndLoadAsync( uploadIntentConfiguration, request.NewPictureIntentId.Value, request.UserId, cancellationToken); if (!validation.IsSuccess) return validation.As(); pictureIntent = validation.Value; playListEntity.OriginalPictureName = pictureIntent.ObjectName; finalPictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(request.UserId, pictureIntent.ObjectName); } database.PlayLists.Update(playListEntity); if (pictureIntent is not null && finalPictureKey is not null) { var publishResult = await PublishUpdatePlayListPictureSourceEventAsync( playListEntity.Id, pictureIntent, finalPictureKey, cancellationToken); if (!publishResult.IsSuccess) return publishResult; } try { await database.SaveChangesAsync(cancellationToken); } catch (Exception exception) { logger.LogError(exception, "Failed to update playlist {PlayListId}", playListEntity.Id); return Result.Error($"Failed to update playlist {playListEntity.Id}"); } logger.LogInformation("Updated playlist {PlayListId}", playListEntity.Id); return Result.Success(PlayListApplicationResponse.FromEntity(playListEntity)); } async Task> PublishUpdatePlayListPictureSourceEventAsync( Guid playListId, UploadIntent pictureIntent, string finalPictureKey, CancellationToken cancellationToken) { try { await eventBus.PublishAsync( new UpdatePlayListPictureSourceEvent( playListId, pictureIntent.Id, storageConfiguration.Bucket, pictureIntent.Key, finalPictureKey, new ImageSize( playListConfiguration.Routes.SmallPicturesPath, playListConfiguration.PicturesSizes.SmallPictureWidth, playListConfiguration.PicturesSizes.SmallPictureHeight), new ImageSize( playListConfiguration.Routes.MediumPicturesPath, playListConfiguration.PicturesSizes.MediumPictureWidth, playListConfiguration.PicturesSizes.MediumPictureHeight), new ImageSize( playListConfiguration.Routes.LargePicturesPath, playListConfiguration.PicturesSizes.LargePictureWidth, playListConfiguration.PicturesSizes.LargePictureHeight)), cancellationToken); return Result.Success(); } catch (Exception exception) { logger.LogError(exception, "Failed to publish update playlist picture source event for playlist {PlayListId}", playListId); return Result.Error($"Failed to publish update playlist picture source event for playlist {playListId}"); } } } }