package com.vibevault.service;

import com.vibevault.dto.PlaylistDTO;
import com.vibevault.dto.SongCreateDTO;
import com.vibevault.dto.SongDTO;
import com.vibevault.exception.ResourceNotFoundException;
import com.vibevault.exception.UnauthorizedException;
import com.vibevault.model.Playlist;
import com.vibevault.model.Song;
import com.vibevault.model.User;
import com.vibevault.repository.PlaylistRepository;
import com.vibevault.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 歌单服务实现
 * 
 * 需要实现：
 * - 所有 PlaylistService 接口中定义的方法
 * - 将实体转换为 DTO 返回给调用者
 * - 资源不存在时抛出 ResourceNotFoundException
 * - [Challenge] 检查用户是否有权限操作歌单（所有权检查）
 */
@Service
public class PlaylistServiceImpl implements PlaylistService {

    private final PlaylistRepository playlistRepository;
    private final UserRepository userRepository;

    public PlaylistServiceImpl(PlaylistRepository playlistRepository, UserRepository userRepository) {
        this.playlistRepository = playlistRepository;
        this.userRepository = userRepository;
    }

    @Override
    public List<PlaylistDTO> getAllPlaylists() {
        return playlistRepository.findAll().stream()
                .map(this::toDTO)
                .toList();
    }

    @Override
    public PlaylistDTO getPlaylistById(Long id) {
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + id));
        return toDTO(playlist);
    }

    @Override
    @Transactional
    public PlaylistDTO createPlaylist(String name, String ownerUsername) {
        User owner = userRepository.findByUsername(ownerUsername)
                .orElseThrow(() -> new ResourceNotFoundException("User not found: " + ownerUsername));
        Playlist playlist = new Playlist(name, owner);
        playlist = playlistRepository.save(playlist);
        return toDTO(playlist);
    }

    @Override
    @Transactional
    public PlaylistDTO addSongToPlaylist(Long playlistId, SongCreateDTO song, String username) {
        Playlist playlist = playlistRepository.findById(playlistId)
                .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId));
        checkPermission(playlist, username);
        Song newSong = new Song(song.title(), song.artist(), song.durationInSeconds());
        playlist.addSong(newSong);
        playlist = playlistRepository.save(playlist);
        return toDTO(playlist);
    }

    @Override
    @Transactional
    public void removeSongFromPlaylist(Long playlistId, Long songId, String username) {
        Playlist playlist = playlistRepository.findById(playlistId)
                .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId));
        checkPermission(playlist, username);
        Song songToRemove = playlist.getSongs().stream()
                .filter(s -> s.getId().equals(songId))
                .findFirst()
                .orElseThrow(() -> new ResourceNotFoundException("Song not found with id: " + songId));
        playlist.removeSong(songToRemove);
        playlistRepository.save(playlist);
    }

    @Override
    @Transactional
    public void deletePlaylist(Long playlistId, String username) {
        Playlist playlist = playlistRepository.findById(playlistId)
                .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId));
        checkPermission(playlist, username);
        playlistRepository.delete(playlist);
    }

    // ========== Advanced 方法 ==========

    @Override
    public List<PlaylistDTO> searchPlaylists(String keyword) {
        return playlistRepository.findByNameContainingIgnoreCase(keyword).stream()
                .map(this::toDTO)
                .toList();
    }

    @Override
    @Transactional
    public PlaylistDTO copyPlaylist(Long playlistId, String newName, String username) {
        Playlist original = playlistRepository.findById(playlistId)
                .orElseThrow(() -> new ResourceNotFoundException("Playlist not found with id: " + playlistId));
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new ResourceNotFoundException("User not found: " + username));
        
        Playlist copy = new Playlist(newName, user);
        for (Song song : original.getSongs()) {
            Song newSong = new Song(song.getTitle(), song.getArtist(), song.getDurationInSeconds());
            copy.addSong(newSong);
        }
        copy = playlistRepository.save(copy);
        return toDTO(copy);
    }

    // ========== 辅助方法 ==========

    /**
     * 将 Playlist 实体转换为 DTO
     */
    private PlaylistDTO toDTO(Playlist playlist) {
        List<SongDTO> songDTOs = playlist.getSongs().stream()
                .map(this::toSongDTO)
                .toList();
        return new PlaylistDTO(
                playlist.getId(),
                playlist.getName(),
                playlist.getOwner().getUsername(),
                songDTOs
        );
    }

    /**
     * 将 Song 实体转换为 DTO
     */
    private SongDTO toSongDTO(Song song) {
        return new SongDTO(
                song.getId(),
                song.getTitle(),
                song.getArtist(),
                song.getDurationInSeconds()
        );
    }

    /**
     * [Challenge] 检查用户是否有权限操作指定歌单
     * 规则：歌单所有者或管理员可以操作
     */
    private void checkPermission(Playlist playlist, String username) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new ResourceNotFoundException("User not found: " + username));
        boolean isOwner = playlist.getOwner().getUsername().equals(username);
        boolean isAdmin = "ROLE_ADMIN".equals(user.getRole());
        if (!isOwner && !isAdmin) {
            throw new UnauthorizedException("You don't have permission to modify this playlist");
        }
    }
}
