DeuspotifyServiceImpl.java

package com.deusto.deuspotify.services;

import com.deusto.deuspotify.model.Song;
import com.deusto.deuspotify.model.Playlist;
import com.deusto.deuspotify.repositories.SongRepository;
import com.deusto.deuspotify.repositories.PlaylistRepository;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
 * @class DeuspotifyServiceImpl
 * @brief Service implementation for managing songs and playlists in the Deuspotify application.
 *
 * This service provides functionality to retrieve, create, update, and delete songs and playlists,
 * and handles operations related to file uploads and custom ordering of playlist contents.
 */
@Service
public class DeuspotifyServiceImpl implements DeuspotifyService {

    private final SongRepository songRepository;
    private final PlaylistRepository playlistRepository;

    private final String uploadDir = "uploads/songs";

    /**
     * Constructor for dependency injection.
     * 
     * @param songRepository Repository for managing Song entities.
     * @param playlistRepository Repository for managing Playlist entities.
     */
    public DeuspotifyServiceImpl(SongRepository songRepository, PlaylistRepository playlistRepository) {
        this.songRepository = songRepository;
        this.playlistRepository = playlistRepository;
    }

    /// @name Song Operations
    /// @{

    /** {@inheritDoc} */
    public List<Song> retrieveAllSongs() {
        return songRepository.findAll();
    }

    /** {@inheritDoc} */
    public Optional<Song> findSong(Long id) {
        return songRepository.findById(id);
    }

    /** {@inheritDoc} */
    public Song addSong(Song song) {
        return songRepository.save(song);
    }

    /** {@inheritDoc} */
    public Song updateSong(Long id, Song song) {
        song.setId(id);
        return songRepository.save(song);
    }

    /** {@inheritDoc} */
    public void deleteSong(Long id) {
        List<Playlist> playlists = playlistRepository.findAll();
        Optional<Song> songOpt = songRepository.findById(id);
        
        if (songOpt.isPresent()) {
            Song song = songOpt.get();
            for (Playlist playlist : playlists) {
                if (playlist.getSongs().contains(song)) {
                    playlist.getSongs().remove(song);
                    playlistRepository.save(playlist);
                }
            }
            songRepository.delete(song);
        } else {
            throw new RuntimeException("Canción no encontrada");
        }
    }

    /**
     * Adds a song with an associated audio file.
     *
     * @param name Song name.
     * @param album Album name.
     * @param artists Comma-separated artist names.
     * @param genres Comma-separated genre names.
     * @param duration Duration of the song in seconds.
     * @param dateOfRelease Release date.
     * @param file Audio file to upload.
     * @return The created Song.
     */
    public Song addSongWithFile(String name, String album, String artists, String genres,
                                double duration, Date dateOfRelease, MultipartFile file) {
        if (file.getContentType() == null || !file.getContentType().startsWith("audio/")) {
            throw new IllegalArgumentException("Only audio files are allowed");
        }
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || !originalFilename.toLowerCase().matches(".*\\.(mp3|wav|ogg|flac)$")) {
            throw new IllegalArgumentException("Invalid audio file extension");
        }

        try {
            Files.createDirectories(Paths.get(uploadDir));
            String cleanedFilename = System.currentTimeMillis() + "_" + originalFilename.replaceAll("[^a-zA-Z0-9\\.\\-]", "_");
            Path filePath = Paths.get(uploadDir, cleanedFilename);
            Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);

            Song song = new Song();
            song.setName(name);
            song.setAlbum(album);
            song.setArtists(Arrays.asList(artists.split("\\s*,\\s*")));
            song.setGenres(Arrays.asList(genres.split("\\s*,\\s*")));
            song.setDuration(duration);
            song.setDateOfRelease(dateOfRelease);
            song.setFilePath(cleanedFilename);

            return songRepository.save(song);
        } catch (IOException e) {
            throw new RuntimeException("Error saving file", e);
        }
    }

    /**
     * Creates a shared song using a provided file path (without uploading).
     *
     * @param name Song name.
     * @param album Album name.
     * @param artists Comma-separated artist names.
     * @param genres Comma-separated genre names.
     * @param duration Song duration in seconds.
     * @param dateOfRelease Release date.
     * @param filePath Path to the existing audio file.
     * @return The created Song.
     */
    public Song createSharedSong(String name, String album, String artists, String genres, double duration, Date dateOfRelease, String filePath) {
        Song sharedSong = new Song();
        sharedSong.setName(name + " Shared");
        sharedSong.setAlbum(album);
        sharedSong.setArtists(new ArrayList<>(Arrays.asList(artists.split("\\s*,\\s*"))));
        sharedSong.setGenres(new ArrayList<>(Arrays.asList(genres.split("\\s*,\\s*"))));
        sharedSong.setDuration(duration);
        sharedSong.setDateOfRelease(dateOfRelease);
        sharedSong.setFilePath(filePath);
        return songRepository.save(sharedSong);
    }

    /// @}
    /// @name Playlist Operations
    /// @{

    /** {@inheritDoc} */
    public List<Playlist> retrieveAllPlaylists() {
        return playlistRepository.findAll();
    }

    /** {@inheritDoc} */
    public Optional<Playlist> findPlaylist(Long id) {
        return playlistRepository.findById(id);
    }

    /** {@inheritDoc} */
    public Playlist addPlaylist(Playlist playlist) {
        return playlistRepository.save(playlist);
    }

    /**
     * Updates a playlist and optionally reorders its songs.
     *
     * @param id Playlist ID.
     * @param playlist Playlist data including optional reordering.
     * @return The updated playlist.
     */
    public Playlist updatePlaylist(Long id, Playlist playlist) {
        playlist.setId(id);

        if (playlist.getSongs() != null && playlist.getOrder() != null) {
            String orderType = playlist.getOrderType();

            if ("duration".equalsIgnoreCase(orderType)) {
                playlist.getSongs().sort((s1, s2) -> Double.compare(s2.getDuration(), s1.getDuration()));
            } else if ("release_date".equalsIgnoreCase(orderType)) {
                playlist.getSongs().sort((s1, s2) -> {
                    if (s1.getDateOfRelease() == null || s2.getDateOfRelease() == null) {
                        return 0;
                    }
                    return s2.getDateOfRelease().compareTo(s1.getDateOfRelease());
                });
            }
        }

        return playlistRepository.save(playlist);
    }

    /** {@inheritDoc} */
    public void deletePlaylist(Long id) {
        playlistRepository.deleteById(id);
    }

    /** {@inheritDoc} */
    public List<Song> findSongsByIds(List<Long> songIds) {
        return songRepository.findAllById(songIds);
    }

    /** {@inheritDoc} */
    public Playlist updatePlaylistSongs(Long playlistId, List<Long> songIds) {
        Optional<Playlist> playlistOptional = playlistRepository.findById(playlistId);
        if (!playlistOptional.isPresent()) {
            throw new RuntimeException("Playlist no encontrada");
        }

        Playlist playlist = playlistOptional.get();
        List<Song> songs = findSongsByIds(songIds);
        playlist.setSongs(songs);

        return playlistRepository.save(playlist);
    }

    /// @}
}