package com.test.musichouduan.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.test.musichouduan.dto.MusicDTO;
import com.test.musichouduan.dto.MusicStatusDTO;
import com.test.musichouduan.dto.PageResult;
import com.test.musichouduan.entity.Music;
import com.test.musichouduan.entity.MusicType;
import com.test.musichouduan.entity.Singer;
import com.test.musichouduan.entity.User;
import com.test.musichouduan.entity.UserCollectMusic;
import com.test.musichouduan.entity.UserLikeMusic;
import com.test.musichouduan.entity.UserMusic;
import com.test.musichouduan.exception.BusinessException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.musichouduan.repository.*;
import com.test.musichouduan.service.FileService;
import com.test.musichouduan.service.MusicService;
import com.test.musichouduan.util.HotScoreUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 音乐服务实现类
 */
@Service
public class MusicServiceImpl implements MusicService {

    // Redis 缓存键
    private static final String HOT_MUSIC_CACHE_KEY = "music:hot_list";
    // Redis 缓存过期时间（小时）
    private static final long CACHE_EXPIRATION_HOURS = 2;
    // 热门音乐列表缓存数量
    private static final int HOT_MUSIC_LIMIT = 50;

    @Autowired
    private MusicRepository musicRepository;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private MusicTypeRepository musicTypeRepository;

    @Autowired
    private SingerRepository singerRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserCollectMusicRepository userCollectMusicRepository;

    @Autowired
    private UserLikeMusicRepository userLikeMusicRepository;

    @Autowired
    private PlayHistoryRepository playHistoryRepository;

    @Autowired
    private FileService fileService;

    @Override
    public MusicDTO getMusicById(Long id, Long userId) {
        // 查找音乐，如果不存在则返回null
        return musicRepository.findById(id)
                .map(music -> convertToDTO(music, userId))
                .orElse(null);
    }

    @Override
    public MusicDTO getMusicById(Long id) {
        // 对于未登录用户或不需要用户状态的场景，直接调用新方法并传入null
        return getMusicById(id, null);
    }

    @Override
    public PageResult<MusicDTO> getMusicList(Integer page, Integer size, String keyword, Long typeId) {
        // 构建查询条件
        Specification<Music> 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.isMember(
                        musicTypeRepository.findById(typeId).orElse(null),
                        root.get("types")
                ));
            }

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

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

        // 转换为DTO
        List<MusicDTO> musicDTOs = musicPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                musicDTOs,
                musicPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    public PageResult<MusicDTO> getMusicListBySinger(Long singerId, Integer page, Integer size) {
        // 获取歌手
        Singer singer = singerRepository.findById(singerId)
                .orElseThrow(() -> new BusinessException("歌手不存在"));

        // 查询歌手的音乐
        Page<Music> musicPage = musicRepository.findBySingersContaining(
                singer,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
        );

        // 转换为DTO
        List<MusicDTO> musicDTOs = musicPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return new PageResult<>(
                musicDTOs,
                musicPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    public List<MusicDTO> getLatestMusic(Integer limit) {
        // 查询最新音乐
        List<Music> musics = musicRepository.findLatest(PageRequest.of(0, limit));

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

    @Override
    public List<MusicDTO> getHotMusic(Integer limit) {
        // 1. 尝试从 Redis 缓存中获取热门音乐列表
        String hotMusicJson = redisTemplate.opsForValue().get(HOT_MUSIC_CACHE_KEY);

        if (StringUtils.hasText(hotMusicJson)) {
            try {
                // 如果缓存命中，则反序列化并返回
                List<MusicDTO> cachedList = objectMapper.readValue(hotMusicJson, new TypeReference<List<MusicDTO>>() {});
                // 根据请求的 limit 参数返回相应数量的音乐
                return cachedList.stream().limit(limit).collect(Collectors.toList());
            } catch (Exception e) {
                // 日志记录反序列化失败
                System.err.println("Failed to deserialize hot music list from Redis: " + e.getMessage());
            }
        }

        // 2. 如果缓存未命中或反序列化失败，则立即计算并阻塞等待结果
        // 这是一个同步调用，确保首次请求能拿到数据
        List<MusicDTO> hotList = calculateAndCacheHotMusicSync();
        return hotList.stream().limit(limit).collect(Collectors.toList());
    }

    /**
     * 同步计算并缓存热门音乐列表。
     * 用于在缓存失效时，立即为用户提供数据。
     * @return 计算后的热门音乐列表
     */
    private List<MusicDTO> calculateAndCacheHotMusicSync() {
        try {
            // 调用异步方法并等待其完成
            return updateHotMusicCache().get();
        } catch (Exception e) {
            // 在异常情况下，返回一个降级方案：例如按播放量排序的列表
            System.err.println("Failed to calculate hot music list synchronously: " + e.getMessage());
            List<Music> musics = musicRepository.findMostPlayed(PageRequest.of(0, HOT_MUSIC_LIMIT));
            return musics.stream()
                    .map(this::convertToDTO)
                    .collect(Collectors.toList());
        }
    }

    /**
     * 定时任务，定期更新热门音乐缓存。
     * cron表达式表示每小时的0分执行 (e.g., 1:00, 2:00, 3:00)
     */
    @Scheduled(cron = "0 0 * * * ?")
    @Override
    public void scheduleUpdateHotMusic() {
        System.out.println("Scheduled task started: Updating hot music cache...");
        updateHotMusicCache();
    }

    /**
     * 异步计算所有音乐的热度，并将热度最高的前50首缓存到Redis。
     * 使用 @Async 注解使其在独立的线程中执行。
     * @return 返回一个 CompletableFuture，包含计算后的热门音乐列表
     */
    @Async
    @Transactional(readOnly = true) // 使用只读事务，提高查询性能
    @Override
    public CompletableFuture<List<MusicDTO>> updateHotMusicCache() {
        // 使用 JOIN FETCH 一次性加载所有音乐及其关联的歌手信息
        List<Music> allMusic = musicRepository.findAllWithSingers();

        // 使用并行流来加速计算过程
        allMusic.parallelStream().forEach(music -> {
            double score = HotScoreUtil.calculateHotScore(music);
            music.setHotScore(score);
        });

        // 对所有音乐按计算出的热度分进行降序排序
        List<MusicDTO> sortedHotMusic = allMusic.stream()
                .sorted(Comparator.comparing(Music::getHotScore).reversed())
                .limit(HOT_MUSIC_LIMIT) // 取热度最高的前50
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        try {
            // 将排序后的列表序列化为JSON字符串
            String jsonToCache = objectMapper.writeValueAsString(sortedHotMusic);
            // 存入Redis，并设置过期时间
            redisTemplate.opsForValue().set(HOT_MUSIC_CACHE_KEY, jsonToCache, CACHE_EXPIRATION_HOURS, TimeUnit.HOURS);
            System.out.println("Hot music cache updated successfully with " + sortedHotMusic.size() + " items.");
        } catch (Exception e) {
            // 日志记录序列化或Redis操作失败
            System.err.println("Failed to cache hot music list to Redis: " + e.getMessage());
        }

        // 返回一个已完成的 CompletableFuture
        return CompletableFuture.completedFuture(sortedHotMusic);
    }

    @Override
    @Transactional
    public MusicDTO addMusic(MusicDTO musicDTO, MultipartFile file, MultipartFile cover) {
        // 1. 先上传音乐文件，获取其物理路径
        String musicPhysicalPath = fileService.uploadMusic(file);
        File musicFile = new File(musicPhysicalPath);

        // 2. 根据物理文件获取时长
        int duration = fileService.getMusicDuration(musicFile);

        // 3. 上传封面
        String coverPhysicalPath = fileService.uploadImage(cover);

        // 4. 将物理路径转换为可访问的URL (这一步需要根据您的配置调整)
        // 假设 uploadPath 是 "music-houduan/upload"，accessPath 是 "/upload/"
        // 物理路径: music-houduan/upload/musics/uuid.mp3
        // URL: /upload/musics/uuid.mp3
        String musicUrl = musicPhysicalPath.replace(System.getProperty("user.dir") + File.separator + "music-houduan", "").replace(File.separator, "/");
        String coverUrl = coverPhysicalPath.replace(System.getProperty("user.dir") + File.separator + "music-houduan", "").replace(File.separator, "/");


        // 创建音乐实体
        Music music = new Music();
        BeanUtils.copyProperties(musicDTO, music);
        music.setUrl(musicUrl);
        music.setCover(coverUrl);
        music.setDuration(duration);
        music.setPlayCount(0L);

        // 设置歌词
        if (StringUtils.hasText(musicDTO.getLyric())) {
            music.setLyric(musicDTO.getLyric());
        }

        // 设置音乐类型
        if (musicDTO.getTypeIds() != null && !musicDTO.getTypeIds().isEmpty()) {
            Set<MusicType> types = new HashSet<>();
            for (Long typeId : musicDTO.getTypeIds()) {
                MusicType type = musicTypeRepository.findById(typeId)
                        .orElseThrow(() -> new BusinessException("音乐类型不存在"));
                types.add(type);
            }
            music.setTypes(types);
        }

        // 设置歌手
        if (musicDTO.getSingerIds() != null && !musicDTO.getSingerIds().isEmpty()) {
            // 如果有歌手ID列表，优先使用
            Set<Singer> singers = new HashSet<>();
            for (Long singerId : musicDTO.getSingerIds()) {
                Singer singer = singerRepository.findById(singerId)
                        .orElseThrow(() -> new BusinessException("歌手不存在"));
                singers.add(singer);
            }
            music.setSingers(singers);
        } else if (StringUtils.hasText(musicDTO.getSinger())) {
            // 如果有歌手名称字符串，则处理歌手名称
            String[] singerNames = musicDTO.getSinger().split("[,，、]");
            Set<Singer> singers = new HashSet<>();
            for (String singerName : singerNames) {
                String trimmedName = singerName.trim();
                if (StringUtils.hasText(trimmedName)) {
                    Optional<Singer> existingSinger = singerRepository.findByName(trimmedName);
                    if (existingSinger.isPresent()) {
                        singers.add(existingSinger.get());
                    } else {
                        Singer newSinger = new Singer();
                        newSinger.setName(trimmedName);
                        singers.add(singerRepository.save(newSinger));
                    }
                }
            }
            music.setSingers(singers);
        }

        // 保存音乐
        Music savedMusic = musicRepository.save(music);

        return convertToDTO(savedMusic);
    }

    @Override
    @Transactional
    public MusicDTO updateMusic(Long id, MusicDTO musicDTO, MultipartFile file, MultipartFile cover, MultipartFile lyric) {
        // 获取音乐
        Music music = musicRepository.findById(id)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        // 如果有新的音乐文件，则上传并更新URL
        if (file != null && !file.isEmpty()) {
            // 删除旧文件
            if (music.getUrl() != null) {
                fileService.deleteFile(music.getUrl());
            }

            // 上传新文件
            String musicPhysicalPath = fileService.uploadMusic(file);
            String musicUrl = musicPhysicalPath.replace(System.getProperty("user.dir") + File.separator + "music-houduan", "").replace(File.separator, "/");
            music.setUrl(musicUrl);
        }

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

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

        // 如果有新的歌词文件，则读取歌词内容
        if (lyric != null && !lyric.isEmpty()) {
            try {
                String lyricContent = new String(lyric.getBytes(), "UTF-8");
                music.setLyric(lyricContent);
            } catch (Exception e) {
                throw new BusinessException("读取歌词文件失败");
            }
        } else if (StringUtils.hasText(musicDTO.getLyric())) {
            // 如果没有歌词文件，但有歌词内容，则直接使用歌词内容
            music.setLyric(musicDTO.getLyric());
        }

        // 更新音乐信息
        music.setName(musicDTO.getName());
        music.setAlbum(musicDTO.getAlbum());
        music.setDuration(musicDTO.getDuration());

        // 更新音乐类型
        if (musicDTO.getTypeIds() != null) {
            Set<MusicType> types = new HashSet<>();
            for (Long typeId : musicDTO.getTypeIds()) {
                MusicType type = musicTypeRepository.findById(typeId)
                        .orElseThrow(() -> new BusinessException("音乐类型不存在"));
                types.add(type);
            }
            music.setTypes(types);
        }

        // 更新歌手
        if (musicDTO.getSingerIds() != null && !musicDTO.getSingerIds().isEmpty()) {
            // 如果有歌手ID列表，优先使用
            Set<Singer> singers = new HashSet<>();
            for (Long singerId : musicDTO.getSingerIds()) {
                Singer singer = singerRepository.findById(singerId)
                        .orElseThrow(() -> new BusinessException("歌手不存在"));
                singers.add(singer);
            }
            music.setSingers(singers);
        } else if (StringUtils.hasText(musicDTO.getSinger())) {
            // 如果有歌手名称字符串，则处理歌手名称
            String[] singerNames = musicDTO.getSinger().split("[,，、]");
            Set<Singer> singers = new HashSet<>();
            for (String singerName : singerNames) {
                String trimmedName = singerName.trim();
                if (StringUtils.hasText(trimmedName)) {
                    Optional<Singer> existingSinger = singerRepository.findByName(trimmedName);
                    if (existingSinger.isPresent()) {
                        singers.add(existingSinger.get());
                    } else {
                        Singer newSinger = new Singer();
                        newSinger.setName(trimmedName);
                        singers.add(singerRepository.save(newSinger));
                    }
                }
            }
            music.setSingers(singers);
        }

        // 保存音乐
        Music updatedMusic = musicRepository.save(music);

        return convertToDTO(updatedMusic);
    }

    @Override
    @Transactional
    public boolean deleteMusic(Long id) {
        // 获取音乐
        Music music = musicRepository.findById(id)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        // 删除音乐文件
        if (music.getUrl() != null) {
            fileService.deleteFile(music.getUrl());
        }

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

        // 删除音乐
        musicRepository.delete(music);

        return true;
    }

    @Override
    @Transactional
    public boolean toggleCollectMusic(Long musicId, Long userId) {
        // 直接通过ID判断是否存在，避免查询完整实体
        boolean isCollected = userCollectMusicRepository.existsByUserIdAndMusicId(userId, musicId);
        
        Music music = musicRepository.findById(musicId)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        if (isCollected) {
            // 已收藏，取消收藏
            User user = userRepository.findById(userId).orElseThrow(() -> new BusinessException("用户不存在"));
            UserCollectMusic collect = userCollectMusicRepository.findByUserAndMusic(user, music)
                .orElseThrow(() -> new BusinessException("收藏记录不存在"));
            userCollectMusicRepository.delete(collect);
            
            int currentCollectCount = music.getCollectCount() != null ? music.getCollectCount() : 0;
            music.setCollectCount(Math.max(0, currentCollectCount - 1));
            musicRepository.save(music);
            return false; // 返回 "未收藏" 状态
        } else {
            // 未收藏，执行收藏
            User user = userRepository.findById(userId).orElseThrow(() -> new BusinessException("用户不存在"));
            UserCollectMusic newCollect = new UserCollectMusic();
            newCollect.setUser(user);
            newCollect.setMusic(music);
            newCollect.setCreateTime(LocalDateTime.now());
            userCollectMusicRepository.save(newCollect);

            int currentCollectCount = music.getCollectCount() != null ? music.getCollectCount() : 0;
            music.setCollectCount(currentCollectCount + 1);
            musicRepository.save(music);
            return true; // 返回 "已收藏" 状态
        }
    }

    @Override
    public PageResult<MusicDTO> getCollectedMusic(Long userId, Integer page, Integer size) {
        // 获取用户
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));

        // 获取用户收藏的音乐
        Page<UserCollectMusic> userCollectMusicPage = userCollectMusicRepository.findByUser(
                user,
                PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createTime"))
        );

        // 转换为DTO
        List<MusicDTO> musicDTOs = userCollectMusicPage.getContent().stream()
                .map(userCollectMusic -> {
                    MusicDTO musicDTO = convertToDTO(userCollectMusic.getMusic());
                    // 设置收藏时间
                    musicDTO.setCollectTime(userCollectMusic.getCreateTime());
                    return musicDTO;
                })
                .collect(Collectors.toList());

        return new PageResult<>(
                musicDTOs,
                userCollectMusicPage.getTotalElements(),
                page,
                size
        );
    }

    @Override
    @Transactional
    public MusicDTO playMusic(Long id) {
        // 获取音乐
        Optional<Music> musicOpt = musicRepository.findById(id);
        if (!musicOpt.isPresent()) {
            // 如果音乐不存在，返回null
            return null;
        }

        Music music = musicOpt.get();

        // 增加播放次数
        long currentPlayCount = music.getPlayCount() != null ? music.getPlayCount() : 0;
        music.setPlayCount(currentPlayCount + 1);
        musicRepository.save(music);

        // 如果用户已登录，记录播放历史
        if (StpUtil.isLogin()) {
            Long userId = StpUtil.getLoginIdAsLong();
            User user = userRepository.findById(userId).orElse(null);
            if (user != null) {
                playHistoryRepository.recordPlayHistory(user.getId(), music.getId());
            }
        }

        return convertToDTO(music);
    }

    @Override
    public Music getById(Long id) {
        return musicRepository.findById(id).orElse(null);
    }

    @Override
    @Transactional
    public boolean toggleLikeMusic(Long musicId, Long userId) {
        Music music = musicRepository.findById(musicId)
                .orElseThrow(() -> new BusinessException("音乐不存在"));

        Optional<UserLikeMusic> likeOpt = userLikeMusicRepository.findByUserIdAndMusicId(userId, musicId);

        if (likeOpt.isPresent()) {
            // 已点赞，取消点赞
            userLikeMusicRepository.delete(likeOpt.get());
            int currentLikeCount = music.getLikeCount() != null ? music.getLikeCount() : 0;
            music.setLikeCount(Math.max(0, currentLikeCount - 1));
            musicRepository.save(music);
            return false; // 返回 "未点赞" 状态
        } else {
            // 未点赞，执行点赞
            UserLikeMusic newLike = new UserLikeMusic();
            newLike.setUserId(userId);
            newLike.setMusicId(musicId);
            newLike.setCreateTime(LocalDateTime.now());
            userLikeMusicRepository.save(newLike);
            int currentLikeCount = music.getLikeCount() != null ? music.getLikeCount() : 0;
            music.setLikeCount(currentLikeCount + 1);
            musicRepository.save(music);
            return true; // 返回 "已点赞" 状态
        }
    }

    @Override
    public MusicStatusDTO getMusicStatus(Long musicId, Long userId) {
        if (userId == null) {
            // 用户未登录，返回默认状态
            return new MusicStatusDTO(false, false);
        }
        boolean isCollected = userCollectMusicRepository.existsByUserIdAndMusicId(userId, musicId);
        boolean isLiked = userLikeMusicRepository.existsByUserIdAndMusicId(userId, musicId);
        return new MusicStatusDTO(isCollected, isLiked);
    }

    @Override
    public Music findMusicById(Integer musicId) {
        return musicRepository.findById(musicId.longValue()).orElse(null);
    }

    /**
     * 将实体转换为DTO
     *
     * @param music 音乐实体
     * @return 音乐DTO
     */
    private MusicDTO convertToDTO(Music music) {
        // 保留此方法以兼容不需要用户状态的转换
        return convertToDTO(music, null);
    }

    /**
     * 将实体转换为DTO，并根据用户ID判断是否收藏和点赞
     *
     * @param music  音乐实体
     * @param userId 用户ID (可以为null)
     * @return 音乐DTO
     */
    private MusicDTO convertToDTO(Music music, Long userId) {
        MusicDTO musicDTO = new MusicDTO();
        BeanUtils.copyProperties(music, musicDTO);

        // 确保专辑字段正确复制
        musicDTO.setAlbum(music.getAlbum());

        // 设置类型ID和名称列表
        if (!CollectionUtils.isEmpty(music.getTypes())) {
            List<String> typeNames = music.getTypes().stream().map(MusicType::getName).collect(Collectors.toList());
            musicDTO.setTypeIds(music.getTypes().stream().map(MusicType::getId).collect(Collectors.toList()));
            musicDTO.setTypeNames(typeNames);
            musicDTO.setTypeName(String.join("、", typeNames));
        }

        // 设置发行日期
        musicDTO.setReleaseDate(music.getCreateTime());

        // 设置歌手ID和名称列表
        if (!CollectionUtils.isEmpty(music.getSingers())) {
            musicDTO.setSingerIds(music.getSingers().stream().map(Singer::getId).collect(Collectors.toList()));
            List<String> singerNames = music.getSingers().stream().map(Singer::getName).collect(Collectors.toList());
            musicDTO.setSingerNames(singerNames);
            musicDTO.setSinger(String.join("、", singerNames));

            // 设置歌手头像
            if (music.getSingers().size() == 1) {
                musicDTO.setSingerAvatar(music.getSingers().iterator().next().getAvatar());
            }
        }

        // 如果传入了用户ID，则查询该用户的收藏和点赞状态
        if (userId != null) {
            boolean isCollected = userCollectMusicRepository.existsByUserIdAndMusicId(userId, music.getId());
            musicDTO.setCollected(isCollected);

            boolean isLiked = userLikeMusicRepository.existsByUserIdAndMusicId(userId, music.getId());
            musicDTO.setLiked(isLiked);
        } else {
            // 如果未传入用户ID，则默认为未收藏和未点赞
            musicDTO.setCollected(false);
            musicDTO.setLiked(false);
        }

        return musicDTO;
    }
}
