using ErrorOr; using Mediator; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Musify.Application.Tracks.Commands; using Musify.Domain.ValueObjects; using Musify.Application.Events; using Musify.Application.Contracts; namespace Musify.Application.Tracks.Handlers { public class DeleteTrackCommandHandler( IDatabase database, IEventBus eventBus, ILogger logger) : ICommandHandler> { public async ValueTask> Handle(DeleteTrackCommand request, CancellationToken cancellationToken) { var track = await database.Tracks.SingleOrDefaultAsync(t => t.Id == request.TrackId, cancellationToken); if (track is null) { logger.LogInformation("Track {TrackId} not found", request.TrackId); return Error.NotFound(); } var isOwner = await database.UserHasTracks .AsNoTracking() .AnyAsync(ut => ut.TrackId == request.TrackId && ut.UserId == request.UserId, cancellationToken); if (!isOwner) { logger.LogWarning("User {UserId} unauthorized to delete track {TrackId}", request.UserId, request.TrackId); return Error.Unauthorized(); } if (track.AudioTranscodeProcessingStatus == ProcessingStatus.Processing || track.AudioTranscodeProcessingStatus == ProcessingStatus.Pending) { logger.LogWarning("Track {TrackId} is currently being processed and cannot be deleted", request.TrackId); return Error.Conflict(description: "Track is currently being processed and cannot be deleted"); } track.LifeCycleStatus = LifeCycleStatus.Removing; try { await eventBus.PublishAsync( new DeleteTrackEvent(track.Id, request.UserId), cancellationToken); } catch (Exception exception) { logger.LogError(exception, "Failed to publish delete track event for track {TrackId}", request.TrackId); return Error.Failure(description: $"Failed to delete track {request.TrackId}"); } try { await database.SaveChangesAsync(cancellationToken); } catch (Exception exception) { logger.LogError(exception, "Failed to mark track {TrackId} as processing before deletion", request.TrackId); return Error.Failure(description: $"Failed to delete track {request.TrackId}"); } return new Success(); } } }