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

import cn.reghao.file.api.dto.VideoMeta;
import cn.reghao.file.api.iface.OssService;
import cn.reghao.jutil.jdk.web.db.Page;
import cn.reghao.jutil.jdk.web.db.PageList;
import cn.reghao.tnb.common.web.WebResult;
import cn.reghao.tnb.content.app.vod.model.dto.VideoUrlDto;
import cn.reghao.tnb.auth.api.iface.AccountQuery;
import cn.reghao.tnb.common.auth.UserContext;
import cn.reghao.tnb.common.util.StringUtil;
import cn.reghao.tnb.content.api.constant.PostScope;
import cn.reghao.tnb.content.api.constant.VideoStatus;
import cn.reghao.tnb.content.api.dto.UserVideoPost;
import cn.reghao.tnb.content.api.dto.VideoCard;
import cn.reghao.tnb.content.api.dto.VideoPostCard;
import cn.reghao.tnb.content.app.vod.db.mapper.BannerVideoMapper;
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.PostCategory;
import cn.reghao.tnb.content.app.vod.model.po.VideoPost;
import cn.reghao.tnb.content.app.vod.model.po.VideoStatistic;
import cn.reghao.tnb.content.app.vod.model.vo.BannerVideoVO;
import cn.reghao.tnb.content.app.vod.model.vo.VideoDetail;
import cn.reghao.tnb.content.app.vod.model.query.VideoQuery;
import cn.reghao.tnb.content.app.vod.service.CategoryService;
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.VideoPostQuery;
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 java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2024-08-06 21:01:22
 */
@Slf4j
@Service
public class VideoPostQueryImpl implements VideoPostQuery {
    @DubboReference(check = false, retries = 0, timeout = 60_000)
    private AccountQuery accountQuery;
    @DubboReference(check = false)
    private UserService userService;
    @DubboReference(check = false)
    private OssService ossService;

    private final int pageSize = 12;
    private final VideoPostMapper videoPostMapper;
    private final ContentPermission contentPermission;
    private final CategoryService categoryService;
    private final VideoRepository videoRepository;
    private final Random random = new SecureRandom();
    private final BannerVideoMapper bannerVideoMapper;
    private final TagService tagService;

    public VideoPostQueryImpl(VideoPostMapper videoPostMapper, BannerVideoMapper bannerVideoMapper,
                              ContentPermission contentPermission, CategoryService categoryService,
                              VideoRepository videoRepository, TagService tagService) {
        this.videoPostMapper = videoPostMapper;
        this.bannerVideoMapper = bannerVideoMapper;
        this.contentPermission = contentPermission;
        this.categoryService = categoryService;
        this.videoRepository = videoRepository;
        this.tagService = tagService;
    }

    public PageList<VideoCard> getCategoryVideos(int categoryId, int pageNumber) {
        PostCategory postCategory = categoryService.getVideoCategory(categoryId);
        if (postCategory == null) {
            return PageList.empty();
        }

        List<Integer> scopes = contentPermission.getUserScopes();
        VideoQuery videoQuery;
        int pid = postCategory.getPid();
        if (pid == 0) {
            videoQuery = new VideoQuery.Builder()
                    .scope(scopes)
                    .categoryPid(categoryId)
                    .groupBy("publish_by")
                    .orderBy("publish_at")
                    .orderDirection("desc")
                    .build();
        } else {
            videoQuery = new VideoQuery.Builder()
                    .scope(scopes)
                    .categoryId(categoryId)
                    .groupBy("publish_by")
                    .orderBy("publish_at")
                    .orderDirection("desc")
                    .build();
        }

        PageList<String> pageList = categoryService.getVideoIdsByCategory(videoQuery, pageNumber, pageSize);
        List<String> videoIds = pageList.getList();
        if (videoIds.isEmpty()) {
            return PageList.empty();
        }

        List<VideoPostCard> list = videoPostMapper.findVideoCardByVideoIds(videoIds);
        List<VideoCard> videoCardList = list.stream()
                .map(videoPostCard -> getVideoCard(videoPostCard, true))
                .collect(Collectors.toList());
        return PageList.pageList(pageNumber, pageSize, (int) pageList.getTotalSize(), videoCardList);
    }

    public VideoDetail getShortVideo() {
        String videoId = getRandomVideoId();
        if (videoId == null) {
            return null;
        }

        VideoPost videoPost = videoPostMapper.findByVideoId(videoId);
        VideoStatistic videoStatistic = videoRepository.getVideoStatistic(videoId);

        long publishBy = videoPost.getPublishBy();
        String publishByStr = accountQuery.getUserIdStr(publishBy);
        VideoDetail videoDetail = new VideoDetail(videoPost, videoStatistic, publishByStr);
        List<String> tags = tagService.getVideoTagNames(videoId);
        videoDetail.setTags(tags);
        return videoDetail;
    }

    private String getRandomVideoId() {
        long loginUser = UserContext.getUserId();
        List<Integer> userScopes = contentPermission.getUserScopes(loginUser);
        List<String> videoIds = videoRepository.getShortVideoIds(userScopes);
        if (videoIds.isEmpty()) {
            return null;
        }

        int min = 0;
        int size = videoIds.size();
        int randomIdx = min + random.nextInt(size-1 - min);
        return videoIds.get(randomIdx);
    }

    @Override
    public PageList<VideoCard> getTagVideos(String tagName, int pageNumber) {
        PageList<String> pageList = tagService.getVideoIdsByTagName(tagName, pageNumber);
        List<String> videoIds = pageList.getList();
        if (videoIds.isEmpty()) {
            return PageList.empty();
        }

        List<VideoPostCard> list = videoPostMapper.findVideoCardByVideoIds(videoIds);
        List<VideoCard> videoCardList = list.stream()
                .map(videoPostCard -> getVideoCard(videoPostCard, true))
                .collect(Collectors.toList());
        return PageList.pageList(pageNumber, pageList.getPageSize(), (int) pageList.getTotalSize(), videoCardList);
    }

    public PageList<VideoCard> getUserVideos(long userId, int page) {
        VideoQuery videoQuery;
        long loginUser = UserContext.getUserId();
        if (loginUser == userId) {
            videoQuery = new VideoQuery.Builder().status(getVideoStatusList()).userId(userId).build();
        } else {
            List<Integer> scopes = contentPermission.getUserScopes();
            videoQuery = new VideoQuery.Builder().scope(scopes).userId(userId).build();
        }

        return getVideoCards(videoQuery, page, false);
    }

    private List<Integer> getVideoStatusList() {
        List<Integer> list = new ArrayList<>();
        for (VideoStatus videoStatus : VideoStatus.values()) {
            list.add(videoStatus.getCode());
        }

        return list;
    }

    private PageList<VideoCard> getVideoCards(VideoQuery videoQuery, int page, boolean user) {
        int total = videoPostMapper.countByCriteria(videoQuery);
        Page page1 = new Page(page, pageSize);
        List<VideoPostCard> list = videoPostMapper.findVideoCardByPage(page1, videoQuery);
        List<VideoCard> list1 = list.stream()
                .map(videoPostCard -> getVideoCard(videoPostCard, user))
                .collect(Collectors.toList());

        return PageList.pageList(page, pageSize, total, list1);
    }

    public List<VideoCard> getVideoCards(List<String> videoIds) {
        List<VideoPostCard> list = videoPostMapper.findVideoCardByVideoIds(videoIds);
        return list.stream().map(videoPostCard -> getVideoCard(videoPostCard, true)).collect(Collectors.toList());
    }

    public VideoCard getVideoCard(VideoPostCard videoPostCard, boolean user) {
        UserCard userCard = null;
        if (user) {
            long userId = videoPostCard.getPublishBy();
            userCard = userService.getUserAvatar(userId);
        }

        String publishAtStr = StringUtil.getPubDateStr(videoPostCard.getPublishAt());
        String durationStr = StringUtil.getTimeStr(videoPostCard.getDuration());
        boolean cached = videoPostCard.getStatus() == VideoStatus.publish.getCode();
        return new VideoCard(videoPostCard, durationStr, cached, publishAtStr, userCard);
    }

    public String getVideoPostData(String videoId) {
        VideoPost videoPost = videoPostMapper.findByVideoId(videoId);
        if (videoPost == null) {
            return WebResult.notFound();
        }

        int status = videoPost.getStatus();
        if (status != VideoStatus.publish.getCode() && status != VideoStatus.noVideoFile.getCode()) {
            return WebResult.notFound();
        }

        long loginUser = UserContext.getUserId();
        long owner = videoPost.getPublishBy();
        int scope = videoPost.getScope();
        if (scope == PostScope.PRIVATE.getCode() && owner != loginUser) {
            return WebResult.notFound();
        }

        boolean hasPermission = contentPermission.hasPermission(owner, scope, loginUser);
        if (!hasPermission) {
            return WebResult.failWithMsg("permission denied");
        }

        long publishBy = videoPost.getPublishBy();
        String publishByStr = accountQuery.getUserIdStr(publishBy);
        VideoStatistic videoStatistic = videoRepository.getVideoStatistic(videoId);
        VideoDetail videoDetail = new VideoDetail(videoPost, videoStatistic, publishByStr);
        List<String> tags = tagService.getVideoTagNames(videoId);
        videoDetail.setTags(tags);
        return WebResult.success(videoDetail);
    }

    public UserVideoPost getUserVideoPost(VideoPost videoPost) {
        String videoId = videoPost.getVideoId();
        String title = videoPost.getTitle();
        String description = videoPost.getDescription();
        String coverUrl = videoPost.getCoverUrl();
        LocalDateTime pubDate = videoPost.getPublishAt();
        int scope = videoPost.getScope();
        int status = videoPost.getStatus();
        int duration = videoPost.getDuration();
        String quality = "";
        String direction = videoPost.getHorizontal() ? "横屏" : "竖屏";
        String codec = "";
        return new UserVideoPost(videoId, title, description, coverUrl, pubDate, scope, status, duration, quality, direction, codec);
    }

    public List<BannerVideoVO> getBannerVideos() {
        List<BannerVideoVO> list = bannerVideoMapper.findBannerVideos();
        return list;
    }

    @Override
    public List<String> getRandomVideoIds(List<Integer> userScopes, int size) {
        if (userScopes.size() == 1 && userScopes.get(0).equals(PostScope.PROTECT.getCode())) {
            return getRandomVipVideoIds();
        }

        return videoPostMapper.findRandomVideoIds(userScopes, size);
    }

    private List<String> getRandomVipVideoIds() {
        VideoQuery videoQuery = new VideoQuery.Builder()
                .status(List.of(VideoStatus.publish.getCode()))
                .scope(List.of(PostScope.PROTECT.getCode()))
                .categoryPid(11)
                .build();
        int total = videoPostMapper.countByCriteria(videoQuery);
        Page page1 = new Page(1, Math.min(total, 100_000));
        List<String> videoIds = videoPostMapper.findVideoPostByPage(page1, videoQuery).stream()
                .map(VideoPost::getVideoId)
                .collect(Collectors.toList());
        if (videoIds.isEmpty()) {
            return videoIds;
        }

        List<String> results = new ArrayList<>();
        int min = 0;
        int size = videoIds.size();
        for (int i = 0; i < pageSize*10; i++) {
            int randomIdx = min + random.nextInt(size-1 - min);
            results.add(videoIds.get(randomIdx));
        }

        return results;
    }

    public List<VideoMeta> getVideoResource(String videoId) {
        VideoPost videoPost = videoPostMapper.findByVideoId(videoId);
        if (videoPost == null) {
            return Collections.emptyList();
        }

        List<VideoUrlDto> list = videoRepository.getVideoUrls(videoId);
        for (VideoUrlDto videoUrlDto : list) {
            String videoFileId = videoUrlDto.getObjectId();
            try {
                VideoMeta videoMeta = ossService.getVideoMeta(videoFileId);
                return videoMeta != null ? List.of(videoMeta) : Collections.emptyList();
            } catch (Exception e) {
                log.error("{}", e.getMessage());
            }
        }

        return Collections.emptyList();
    }


    /**
     * 获取用户发布的视频稿件
     *
     * @param
     * @return
     * @date 2023-11-27 11:01:22
     */
    public PageList<UserVideoPost> getUserVideoPost(int pageNumber) {
        Page page1 = new Page(pageNumber, pageSize);
        long userId = UserContext.getUserId();

        VideoQuery videoQuery = new VideoQuery.Builder().status(getStatusList()).userId(userId).build();
        int total = videoPostMapper.countByCriteria(videoQuery);
        List<UserVideoPost> list = videoPostMapper.findVideoPostByPage(page1, videoQuery).stream()
                .map(this::getUserVideoPost)
                .collect(Collectors.toList());
        return PageList.pageList(pageNumber, pageSize, total, list);
    }

    private List<Integer> getStatusList() {
        List<Integer> list = new ArrayList<>();
        for (VideoStatus videoStatus : VideoStatus.values()) {
            list.add(videoStatus.getCode());
        }

        return list;
    }

    public UserVideoPost getUserVideoDetail(String videoId) {
        VideoPost videoPost = videoPostMapper.findByVideoId(videoId);
        return getUserVideoPost(videoPost);
    }
}
