package cn.reghao.tnb.content.app.vod.service.impl;

import cn.reghao.file.api.iface.OssService;
import cn.reghao.tnb.content.app.vod.model.dto.VideoUrlDto;
import cn.reghao.tnb.common.auth.UserContext;
import cn.reghao.tnb.common.db.PageScroll;
import cn.reghao.tnb.common.util.ConstantId;
import cn.reghao.tnb.content.api.constant.PostScope;
import cn.reghao.tnb.content.api.dto.VideoRecord;
import cn.reghao.tnb.content.api.dto.VideoUrl;
import cn.reghao.tnb.content.api.dto.VideoUrls;
import cn.reghao.tnb.content.app.vod.db.mapper.PlayCompleteMapper;
import cn.reghao.tnb.content.app.vod.db.mapper.PlayRecordMapper;
import cn.reghao.tnb.content.app.vod.db.mapper.VideoPostMapper;
import cn.reghao.tnb.content.app.vod.db.repository.VideoRepository;
import cn.reghao.tnb.content.app.vod.model.po.PlayComplete;
import cn.reghao.tnb.content.app.vod.model.po.PlayRecord;
import cn.reghao.tnb.content.app.vod.model.po.VideoPost;
import cn.reghao.tnb.content.api.dto.VideoRecordCard;
import cn.reghao.tnb.content.app.vod.service.ContentPermission;
import cn.reghao.tnb.content.app.vod.service.TagService;
import cn.reghao.tnb.content.app.vod.service.VideoPlayService;
import cn.reghao.tnb.content.app.vod.service.rcmd.UserInterestBased;
import cn.reghao.tnb.message.api.dto.MediaProgress;
import cn.reghao.tnb.user.api.dto.UserCard;
import cn.reghao.tnb.user.api.iface.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2024-08-07 07:22:34
 */
@Slf4j
@Service
public class VideoPlayServiceImpl implements VideoPlayService {
    @DubboReference(check = false, timeout = 60_000, retries = 0)
    private OssService ossService;
    @DubboReference(check = false)
    private UserService userService;

    private final int pageSize = 10;
    private final VideoPostMapper videoPostMapper;
    private final PlayRecordMapper playRecordMapper;
    private final VideoRepository videoRepository;
    private final UserInterestBased userInterestBased;
    private final ContentPermission contentPermission;
    private final PlayCompleteMapper playCompleteMapper;
    private final TagService tagService;

    public VideoPlayServiceImpl(VideoPostMapper videoPostMapper, PlayRecordMapper playRecordMapper,
                                VideoRepository videoRepository, UserInterestBased userInterestBased,
                                ContentPermission contentPermission, PlayCompleteMapper playCompleteMapper,
                                TagService tagService) {
        this.videoPostMapper = videoPostMapper;
        this.playRecordMapper = playRecordMapper;
        this.videoRepository = videoRepository;
        this.userInterestBased = userInterestBased;
        this.contentPermission = contentPermission;
        this.playCompleteMapper = playCompleteMapper;
        this.tagService = tagService;
    }

    public VideoUrls getVideoPlayData(String videoId) {
        double currentTime = 0.0;
        long loginUser = UserContext.getUserId();
        if (loginUser != ConstantId.ANONYMOUS_USER_ID) {
            currentTime = getCurrentTime(loginUser, videoId);
        }

        VideoPost videoPost = videoPostMapper.findByVideoId(videoId);
        List<VideoUrlDto> list = videoRepository.getVideoUrls(videoId);
        if (list.isEmpty()) {
            return new VideoUrls();
        }

        String urlType = list.get(0).getType();
        int scope = videoPost.getScope();
        Set<Integer> userScopes = new HashSet<>(contentPermission.getUserScopes());
        long publishBy = videoPost.getPublishBy();
        if (loginUser == publishBy) {
            userScopes.add(PostScope.PRIVATE.getCode());
        }

        // 判断当前请求是否具备访问 video 需要的权限
        if (!userScopes.contains(scope) && loginUser != publishBy) {
            return new VideoUrls();
        }

        if (scope != PostScope.PUBLIC.getCode()) {
            try {
                List<VideoUrl> urls = list.stream().map(videoUrlDto -> {
                    try {
                        int channelCode = videoUrlDto.getChannelCode();
                        String objectUrl = videoUrlDto.getUrl();
                        String objectId = videoUrlDto.getObjectId();
                        String quality = videoUrlDto.getQuality();
                        String signedUrl = ossService.getSignedUrl(objectId, objectUrl);
                        return new VideoUrl(quality, signedUrl);
                    } catch (Exception e) {
                        log.error("{}", e.getMessage());
                    }
                    return null;
                }).filter(Objects::nonNull).collect(Collectors.toList());
                return new VideoUrls(currentTime, urlType, urls);
            } catch (Exception e) {
                log.error("{}", e.getMessage());
            }
            return new VideoUrls();
        } else {
            List<VideoUrl> urls = list.stream().map(videoUrlDto -> {
                String quality = videoUrlDto.getQuality();
                String url = videoUrlDto.getUrl();
                return new VideoUrl(quality, url);
            }).collect(Collectors.toList());
            return new VideoUrls(currentTime, urlType, urls);
        }
    }

    private Double getCurrentTime(long userId, String videoId) {
        Double currentTime = playRecordMapper.findCurrentTime(userId, videoId);
        return currentTime != null ? currentTime : 0.0;
    }

    @Transactional(rollbackFor = Exception.class)
    public synchronized void saveRecord(MediaProgress mediaProgress) {
        long userId = mediaProgress.getUserId();
        String videoId = mediaProgress.getMediaId();
        double currentTime = mediaProgress.getCurrentTime();
        long createAt = System.currentTimeMillis();
        boolean ended = mediaProgress.isEnded();

        PlayRecord playRecord = playRecordMapper.findByUserIdAndVideoId(userId, videoId);
        if (playRecord == null) {
            playRecord = new PlayRecord(userId, mediaProgress);
            playRecordMapper.save(playRecord);
        } else {
            playRecord.setCurrentTime(currentTime);
            playRecord.setCreateAt(createAt);
            playRecordMapper.update(playRecord);

            if (ended) {
                PlayComplete playComplete = new PlayComplete(playRecord);
                playCompleteMapper.save(playComplete);

                addUserInterest(userId, videoId);
            }
        }
    }

    private void addUserInterest(long userId, String videoId) {
        List<String> tagIds = tagService.getVideoTagNames(videoId);
        tagIds.forEach(tagId -> userInterestBased.addUserInterests(userId, tagId));
    }

    public PageScroll<VideoRecordCard> getPlayRecord(long nextId, long userId) {
        if (nextId == 0) {
            Long maxId = playRecordMapper.findMaxId(userId);
            if (maxId == null) {
                return PageScroll.empty();
            }

            nextId = maxId+1;
        }

        int total = playRecordMapper.countUserRecord(userId);
        List<VideoRecord> list = playRecordMapper.findUserRecordByPage1(pageSize, nextId, userId);
        List<VideoRecordCard> list1 = list.stream().map(videoRecord -> {
            long publishBy = videoRecord.getPublishBy();
            UserCard userCard = userService.getUserAvatar(publishBy);
            return new VideoRecordCard(videoRecord, userCard);
        }).collect(Collectors.toList());

        long nextId1;
        if (!list.isEmpty()) {
            nextId1 = list.get(list.size()-1).getCreateAt();
        } else {
            nextId1 = 0;
        }

        return PageScroll.pageList(total, String.valueOf(nextId), String.valueOf(nextId1), list1);
    }
}
