package com.test.musichouduan.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.test.musichouduan.dto.MusicDTO;
import com.test.musichouduan.dto.PageResult;
import com.test.musichouduan.dto.PlaylistDTO;
import com.test.musichouduan.dto.UserDTO;
import com.test.musichouduan.entity.Music;
import com.test.musichouduan.entity.Playlist;
import com.test.musichouduan.entity.PlaylistType;
import com.test.musichouduan.entity.User;
import com.test.musichouduan.exception.BusinessException;
import com.test.musichouduan.repository.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.musichouduan.service.FileService;
import com.test.musichouduan.service.PlaylistService;
import com.test.musichouduan.util.HotPlaylistScoreUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.CollectionUtils;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.Comparator;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import jakarta.persistence.criteria.Predicate;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 歌单服务实现类
 */
@Service
public class PlaylistServiceImpl implements PlaylistService {

    private static final String HOT_PLAYLIST_CACHE_KEY = "playlist:hot_list";
    private static final long CACHE_EXPIRATION_HOURS = 2;
    private static final int HOT_PLAYLIST_LIMIT = 50;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private PlaylistRepository playlistRepository;

    @Autowired
    private PlaylistTypeRepository playlistTypeRepository;

    @Autowired
    private MusicRepository musicRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserPlaylistRepository userPlaylistRepository;

    @Autowired
    private PlayHistoryRepository playHistoryRepository;

    @Autowired
    private FileService fileService;

    @Override
    public PlaylistDTO getPlaylistById(Long id) {
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));
        return convertToDTO(playlist);
    }

    @Override
    public PageResult<PlaylistDTO> getPlaylistList(Integer page, Integer size, String keyword, Long typeId) {
        // 构建查询条件
        Specification<Playlist> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 关键字查询
            if (StringUtils.hasText(keyword)) {
                predicates.add(cb.like(root.get("name"), "%" + keyword + "%"));
            }

            // 类型查询
            if (typeId != null) {
                predicates.add(cb.equal(root.get("type").get("id"), typeId));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        };

        // 执行查询
        Page<Playlist> playlistPage = playlistRepository.findAll(
                spec,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
        );

        // 转换为DTO
        List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                playlistDTOs,
                playlistPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    public List<PlaylistDTO> getLatestPlaylist(Integer limit) {
        // 查询最新歌单
        List<Playlist> playlists = playlistRepository.findLatest(PageRequest.of(0, limit));

        // 转换为DTO
        return playlists.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<PlaylistDTO> getHotPlaylist(Integer limit) {
        String hotPlaylistJson = redisTemplate.opsForValue().get(HOT_PLAYLIST_CACHE_KEY);

        if (StringUtils.hasText(hotPlaylistJson)) {
            try {
                List<PlaylistDTO> cachedList = objectMapper.readValue(hotPlaylistJson, new TypeReference<List<PlaylistDTO>>() {});
                return cachedList.stream().limit(limit).collect(Collectors.toList());
            } catch (Exception e) {
                System.err.println("Failed to deserialize hot playlist list from Redis: " + e.getMessage());
            }
        }

        List<PlaylistDTO> hotList = calculateAndCacheHotPlaylistSync();
        return hotList.stream().limit(limit).collect(Collectors.toList());
    }

    private List<PlaylistDTO> calculateAndCacheHotPlaylistSync() {
        try {
            return updateHotPlaylistCache().get();
        } catch (Exception e) {
            System.err.println("Failed to calculate hot playlist list synchronously: " + e.getMessage());
            List<Playlist> playlists = playlistRepository.findMostPlayed(PageRequest.of(0, HOT_PLAYLIST_LIMIT));
            return playlists.stream()
                    .map(this::convertToDTO)
                    .collect(Collectors.toList());
        }
    }

    @Scheduled(cron = "0 0 * * * ?")
    public void scheduleUpdateHotPlaylist() {
        System.out.println("Scheduled task started: Updating hot playlist cache...");
        updateHotPlaylistCache();
    }

    @Async
    @Transactional(readOnly = true)
    public CompletableFuture<List<PlaylistDTO>> updateHotPlaylistCache() {
        List<Playlist> allPlaylists = new ArrayList<>();
        Page<Playlist> playlistPage;
        int pageNum = 0;
        int pageSize = 500;

        do {
            Pageable pageable = PageRequest.of(pageNum, pageSize);
            playlistPage = playlistRepository.findAll(pageable);
            allPlaylists.addAll(playlistPage.getContent());
            pageNum++;
        } while (playlistPage.hasNext());

        allPlaylists.parallelStream().forEach(playlist -> {
            double score = HotPlaylistScoreUtil.calculateHotScore(playlist);
            playlist.setHotScore(score);
        });

        List<PlaylistDTO> sortedHotPlaylists = allPlaylists.stream()
                .sorted(Comparator.comparing(Playlist::getHotScore).reversed())
                .limit(HOT_PLAYLIST_LIMIT)
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        try {
            String jsonToCache = objectMapper.writeValueAsString(sortedHotPlaylists);
            redisTemplate.opsForValue().set(HOT_PLAYLIST_CACHE_KEY, jsonToCache, CACHE_EXPIRATION_HOURS, TimeUnit.HOURS);
            System.out.println("Hot playlist cache updated successfully with " + sortedHotPlaylists.size() + " items.");
        } catch (Exception e) {
            System.err.println("Failed to cache hot playlist list to Redis: " + e.getMessage());
        }

        return CompletableFuture.completedFuture(sortedHotPlaylists);
    }

    @Override
    public PageResult<PlaylistDTO> getOfficialPlaylist(Integer page, Integer size) {
        // 查询官方歌单
        Page<Playlist> playlistPage = playlistRepository.findByIsOfficial(
                1,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
        );

        // 转换为DTO
        List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                playlistDTOs,
                playlistPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    @Transactional
    public PlaylistDTO addPlaylist(PlaylistDTO playlistDTO, MultipartFile cover) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));

        // 上传封面
        String coverUrl = null;
        if (cover != null && !cover.isEmpty()) {
            String coverPhysicalPath = fileService.uploadImage(cover);
            coverUrl = coverPhysicalPath.replace(System.getProperty("user.dir") + File.separator + "music-houduan", "").replace(File.separator, "/");
        }

        // 创建歌单实体
        Playlist playlist = new Playlist();
        playlist.setName(playlistDTO.getName());
        playlist.setDescription(playlistDTO.getDescription());
        playlist.setCover(coverUrl);
        playlist.setCreator(user);
        playlist.setPlayCount(0L);
        playlist.setCollectCount(0);
        playlist.setIsOfficial(0); // 默认为非官方歌单

        // 设置歌单类型
        if (playlistDTO.getTypeId() != null) {
            PlaylistType type = playlistTypeRepository.findById(playlistDTO.getTypeId())
                    .orElseThrow(() -> new BusinessException("歌单类型不存在"));
            playlist.setType(type);
        }

        // 保存歌单
        Playlist savedPlaylist = playlistRepository.save(playlist);

        return convertToDTO(savedPlaylist);
    }

    @Override
    @Transactional
    public PlaylistDTO updatePlaylist(Long id, PlaylistDTO playlistDTO, MultipartFile cover) {
        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查是否是歌单创建者或管理员
        if (!playlist.getCreator().getId().equals(userId) && !StpUtil.hasRole("admin")) {
            throw new BusinessException("无权修改该歌单");
        }

        // 如果有新的封面，则上传并更新URL
        if (cover != null && !cover.isEmpty()) {
            // 删除旧封面
            if (playlist.getCover() != null) {
                fileService.deleteFile(playlist.getCover());
            }

            // 上传新封面
            String coverPhysicalPath = fileService.uploadImage(cover);
            String coverUrl = coverPhysicalPath.replace(System.getProperty("user.dir") + File.separator + "music-houduan", "").replace(File.separator, "/");
            playlist.setCover(coverUrl);
        }

        // 更新歌单信息
        playlist.setName(playlistDTO.getName());
        playlist.setDescription(playlistDTO.getDescription());

        // 更新是否官方字段
        System.out.println("接收到的isOfficial值: " + playlistDTO.getIsOfficial());
        playlist.setIsOfficial(playlistDTO.getIsOfficial());
        System.out.println("更新后的歌单isOfficial值: " + playlist.getIsOfficial());

        // 更新歌单类型
        if (playlistDTO.getTypeId() != null) {
            PlaylistType type = playlistTypeRepository.findById(playlistDTO.getTypeId())
                    .orElseThrow(() -> new BusinessException("歌单类型不存在"));
            playlist.setType(type);
        }

        // 保存歌单
        Playlist updatedPlaylist = playlistRepository.save(playlist);

        return convertToDTO(updatedPlaylist);
    }

    @Override
    @Transactional
    public boolean deletePlaylist(Long id) {
        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查是否是歌单创建者或管理员
        if (!playlist.getCreator().getId().equals(userId) && !StpUtil.hasRole("admin")) {
            throw new BusinessException("无权删除该歌单");
        }

        // 删除封面
        if (playlist.getCover() != null) {
            fileService.deleteFile(playlist.getCover());
        }

        // 删除歌单
        playlistRepository.delete(playlist);

        return true;
    }

    @Override
    @Transactional
    public boolean collectPlaylist(Long id) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));

        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 检查是否已收藏
        if (userPlaylistRepository.existsByUserAndPlaylist(user, playlist)) {
            throw new BusinessException("已收藏该歌单");
        }

        // 收藏歌单
        userPlaylistRepository.collectPlaylist(user.getId(), playlist.getId());

        // 更新收藏数
        playlist.setCollectCount((playlist.getCollectCount() == null ? 0 : playlist.getCollectCount()) + 1);
        playlistRepository.save(playlist);

        return true;
    }

    @Override
    @Transactional
    public boolean uncollectPlaylist(Long id) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));

        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 检查是否已收藏
        if (!userPlaylistRepository.existsByUserAndPlaylist(user, playlist)) {
            throw new BusinessException("未收藏该歌单");
        }

        // 取消收藏歌单
        userPlaylistRepository.uncollectPlaylist(user.getId(), playlist.getId());

        // 更新收藏数
        playlist.setCollectCount((playlist.getCollectCount() == null ? 0 : playlist.getCollectCount()) - 1);
        playlistRepository.save(playlist);

        return true;
    }

    @Override
    public boolean isCollected(Long id) {
        if (!StpUtil.isLogin()) {
            return false;
        }
        Long userId = StpUtil.getLoginIdAsLong();
        return userPlaylistRepository.existsByUserIdAndPlaylistId(userId, id);
    }

    @Override
    public PageResult<PlaylistDTO> getCollectedPlaylist(Integer page, Integer size) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 获取用户收藏的歌单
        Page<Playlist> playlistPage = userPlaylistRepository.findCollectedPlaylist(
                userId,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "collectTime"))
        );

        // 转换为DTO
        List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                playlistDTOs,
                playlistPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    public PageResult<PlaylistDTO> getUserPlaylist(Integer page, Integer size) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));

        // 获取用户创建的歌单
        Page<Playlist> playlistPage = playlistRepository.findByCreator(
                user,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
        );

        // 转换为DTO
        List<PlaylistDTO> playlistDTOs = playlistPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                playlistDTOs,
                playlistPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    @Transactional
    public boolean addMusicToPlaylist(Long id, Long musicId) {
        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查是否是歌单创建者
        if (!playlist.getCreator().getId().equals(userId)) {
            throw new BusinessException("无权修改该歌单");
        }

        // 获取音乐
        Music music = musicRepository.findById(musicId)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        // 检查音乐是否已在歌单中
        if (playlist.getMusics().contains(music)) {
            throw new BusinessException("该音乐已在歌单中");
        }

        // 添加音乐到歌单
        playlist.getMusics().add(music);
        playlistRepository.save(playlist);

        return true;
    }

    @Override
    @Transactional
    public boolean removeMusicFromPlaylist(Long id, Long musicId) {
        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查是否是歌单创建者
        if (!playlist.getCreator().getId().equals(userId)) {
            throw new BusinessException("无权修改该歌单");
        }

        // 获取音乐
        Music music = musicRepository.findById(musicId)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        // 检查音乐是否在歌单中
        if (!playlist.getMusics().contains(music)) {
            throw new BusinessException("该音乐不在歌单中");
        }

        // 从歌单中移除音乐
        playlist.getMusics().remove(music);
        playlistRepository.save(playlist);

        return true;
    }

    @Override
    @Transactional
    public PlaylistDTO playPlaylist(Long id) {
        System.out.println("接收到播放歌单请求，歌单ID: " + id);
        // 获取歌单
        Playlist playlist = playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));

        // 增加播放次数
        long currentPlayCount = playlist.getPlayCount() == null ? 0 : playlist.getPlayCount();
        System.out.println("当前播放次数: " + currentPlayCount);
        playlist.setPlayCount(currentPlayCount + 1);
        playlistRepository.save(playlist);
        System.out.println("播放次数已增加，更新后为: " + playlist.getPlayCount());

        return convertToDTO(playlist);
    }

    @Override
    public Playlist getById(Long id) {
        return playlistRepository.findById(id)
                .orElseThrow(() -> new BusinessException("歌单不存在"));
    }

    /**
     * 将实体转换为DTO
     *
     * @param playlist 歌单实体
     * @return 歌单DTO
     */
    private PlaylistDTO convertToDTO(Playlist playlist) {
        PlaylistDTO playlistDTO = new PlaylistDTO();
        BeanUtils.copyProperties(playlist, playlistDTO);
        playlistDTO.setHotScore(playlist.getHotScore());
        if (playlist.getPlayCount() == null) {
            playlistDTO.setPlayCount(0L);
        }

        // 设置创建者信息
        if (playlist.getCreator() != null) {
            UserDTO creatorDTO = new UserDTO();
            BeanUtils.copyProperties(playlist.getCreator(), creatorDTO);
            playlistDTO.setCreator(creatorDTO);
        }

        // 设置类型信息
        if (playlist.getType() != null) {
            playlistDTO.setTypeId(playlist.getType().getId());
            playlistDTO.setTypeName(playlist.getType().getName());
        }

        // 设置音乐列表
        if (playlist.getMusics() != null && !playlist.getMusics().isEmpty()) {
            List<MusicDTO> musicDTOs = playlist.getMusics().stream()
                    .map(music -> {
                        MusicDTO musicDTO = new MusicDTO();
                        BeanUtils.copyProperties(music, musicDTO);
                        return musicDTO;
                    })
                    .collect(Collectors.toList());
            playlistDTO.setMusics(musicDTOs);
            playlistDTO.setMusicCount(musicDTOs.size());
        } else {
            playlistDTO.setMusicCount(0);
        }

        // 检查当前登录用户是否已收藏该歌单
        if (StpUtil.isLogin()) {
            Long userId = StpUtil.getLoginIdAsLong();
            boolean isCollected = userPlaylistRepository.existsByUserIdAndPlaylistId(userId, playlist.getId());
            playlistDTO.setIsCollected(isCollected);
        } else {
            playlistDTO.setIsCollected(false);
        }

        return playlistDTO;
    }
}
