package com.tangguo.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.dfa.FoundWord;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tangguo.common.core.domain.entity.SysUser;
import com.tangguo.common.enums.*;
import com.tangguo.common.exception.ServiceException;
import com.tangguo.common.listener.OperateEvent;
import com.tangguo.common.utils.SecurityUtils;
import com.tangguo.common.utils.SensitiveWordUtils;
import com.tangguo.domain.BbsMoment;
import com.tangguo.domain.BbsMomentAttachment;
import com.tangguo.domain.BbsMomentComment;
import com.tangguo.domain.BbsMomentVoteOption;
import com.tangguo.domain.bo.*;
import com.tangguo.domain.vo.*;
import com.tangguo.mapper.BbsMomentMapper;
import com.tangguo.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 动态Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-01
 */
@Slf4j
@Service
public class BbsMomentServiceImpl extends ServiceImpl<BbsMomentMapper, BbsMoment> implements IBbsMomentService {

    @Resource
    private ApplicationEventPublisher eventPublisher;

    @Resource
    private IBbsMomentAttachmentService attachmentsService;

    @Resource
    private IBbsMomentLikeService likeService;

    @Resource
    private IBbsMomentVoteService voteService;

    @Resource
    private IBbsMomentVoteOptionService voteOptionService;

    @Resource
    private IBbsMomentCommentService commentService;


    /**
     * 查询动态列表
     *
     * @param moment 动态
     * @return 动态
     */
    @Override
    public List<BbsMoment> selectBbsMomentList(BbsMoment moment) {
        List<BbsMoment> dbMoments = this.baseMapper.selectBbsMomentList(moment);

        List<Long> momentIds = dbMoments.stream().map(BbsMoment::getId).collect(Collectors.toList());
        List<BbsMomentAttachment> dbAtts = this.attachmentsService.getAttachments(momentIds);

        for (BbsMoment dbMoment : dbMoments) {
            List<BbsMomentAttachment> atts = dbAtts.stream()
                .filter(a -> dbMoment.getId().equals(a.getMomentId()))
                .collect(Collectors.toList());
            dbMoment.setAttachments(atts);
        }
        return dbMoments;
    }


    /**
     * 查询动态详情
     *
     * @param momentId 动态Id
     * @return 动态列表
     */
    @Override
    public BbsMomentListVO selectBbsMoment(Long momentId) {
        String userName = SecurityUtils.getUsername();
        BbsMomentListVO dbMoment = this.baseMapper.selectMomentDetail(momentId, userName);
        this.fillMoment(Collections.singletonList(dbMoment), userName);
        dbMoment.setComments(null);
        return dbMoment;
    }


    /**
     * 删除动态
     *
     * @param id 动态Id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteMoment(Long id) {
        BbsMoment dbMoment = this.getById(id);
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("删除失败，未查询到当前动态数据。");
        }

        Long dbMomentId = dbMoment.getId();
        this.removeById(dbMomentId);
        this.attachmentsService.deleteAttachments(dbMomentId);
        this.voteService.deleteMomentVotes(dbMomentId);
        this.voteOptionService.deleteMomentVoteOptions(dbMomentId);
        this.commentService.deleteMomentComments(dbMomentId);
    }



    /**
     * 查询用户动态统计
     *
     * @return 动态统计
     */
    @Override
    public BbsUserMomentCountVO getUserMomentCount(String userName) {
        return this.baseMapper.selectUserMomentCount(userName);
    }


    /**
     * 查询动态列表
     *
     * @param userName 用户名
     * @param content  动态内容
     * @return 动态列表
     */
    @Override
    public List<BbsMomentListVO> getUserMoments(String userName, String content) {
        List<BbsMomentListVO> dbMoments = this.baseMapper.selectUserMoments(userName, content);
        this.fillMoment(dbMoments, userName);
        return dbMoments;
    }


    /**
     * 查询动态列表
     *
     * @param bo  查询参数
     * @return 动态列表
     */
    @Override
    public List<BbsMomentListVO> getMoments(QueryMomentBO bo) {
        String userName = SecurityUtils.getUsername();
        List<BbsMomentListVO> dbMoments = this.baseMapper.selectMoments(userName, bo);
        this.fillMoment(dbMoments, userName);
        return dbMoments;
    }


    /**
     * 查询动态详情
     *
     * @param momentId 动态Id
     * @return 动态列表
     */
    @Override
    public BbsMomentListVO getMomentDetail(Long momentId) {
        String userName = SecurityUtils.getUsername();
        BbsMomentListVO dbMoment = this.baseMapper.selectMomentDetail(momentId, userName);
        this.fillMoment(Collections.singletonList(dbMoment), userName);
        dbMoment.setComments(null);
        return dbMoment;
    }


    /**
     * 创建动态
     *
     * @param bo 动态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userCreateMoment(CreateMomentBO bo) {
        // 添加动态
        String username = SecurityUtils.getUsername();
        BbsMoment newMoment = this.buildMoment(bo);
        newMoment.setUserName(username);
        this.save(newMoment);

        // 添加动态附件
        MomentType momentType = MomentType.getMomentType(bo.getType());
        if (MomentType.IMAGE == momentType || MomentType.VIDEO == momentType) {
            this.attachmentsService.addAttachments(newMoment, bo.getAttachments());
        }

        // 添加动态投票选项
        if (EnableStatus.QY.getStatus() == bo.getIsEnableVote()) {
            List<CreateMomentBO.VoteOption> voteOptions = bo.getVoteOptions();
            this.voteOptionService.addVoteOptions(newMoment, voteOptions);
        }

        // 发布用户操作事件
        OperateEvent operateEvent = new OperateEvent(username, OperateType.FBDT);
        this.eventPublisher.publishEvent(operateEvent);
    }


    /**
     * 删除动态
     *
     * @param momentId 动态Id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userDeleteMoment(Long momentId) {
        String username = SecurityUtils.getUsername();
        BbsMoment dbMoment = this.getById(momentId);
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("删除失败，未查询到当前动态数据。");
        }
        if (!dbMoment.getUserName().equals(username)) {
            throw new ServiceException("删除失败，没有对当前动态数据的操作权限。");
        }

        // 删除动态以及相关数据
        this.removeById(dbMoment.getId());
        this.attachmentsService.deleteAttachments(dbMoment.getId());
        this.voteService.deleteMomentVotes(dbMoment.getId());
        this.voteOptionService.deleteMomentVoteOptions(dbMoment.getId());
        this.commentService.deleteMomentComments(dbMoment.getId());

        // 发布用户操作事件
        OperateEvent operateEvent = new OperateEvent(username, OperateType.SCDT);
        this.eventPublisher.publishEvent(operateEvent);
    }


    /**
     * 点赞动态
     *
     * @param bo 动态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userLikeMoment(LikeMomentBO bo) {
        BbsMoment dbMoment = this.getById(bo.getMomentId());
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("点赞失败，未查询到当前动态数据。");
        }

        Long dbMomentId = dbMoment.getId();
        String username = SecurityUtils.getUsername();
        long likeStatus = this.likeService.getUserMomentLikeCount(dbMomentId, username);

        if (LikeStatus.LIKE.getStatus() == bo.getLikeStatus()) {
            if (likeStatus > 0) {
                throw new ServiceException("点赞失败，已对当前动态点赞。");
            } else {
                this.likeService.addMomentLike(dbMomentId, username);
                this.baseMapper.incrMomentLikeCount(dbMomentId, 1);
            }
        }
        else {
            if (likeStatus < 1) {
                throw new ServiceException("取消失败，没有对当前动态点赞。");
            } else {
                this.likeService.deleteMomentLike(dbMomentId, username);
                this.baseMapper.decrMomentLikeCount(dbMomentId, 1);
            }
        }

        // 发布用户操作事件
        OperateEvent operateEvent = new OperateEvent(username, OperateType.DZDT);
        this.eventPublisher.publishEvent(operateEvent);
    }


    /**
     * 投票动态
     *
     * @param bo 动态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userVoteMoment(VoteMomentBO bo) {
        BbsMoment dbMoment = this.getById(bo.getMomentId());
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("投票失败，未查询到当前动态数据。");
        }
        if (EnableStatus.QY.getStatus() != dbMoment.getIsEnableVote()) {
            throw new ServiceException("投票失败，当前动态未开启投票。");
        }

        Long dbMomentId = dbMoment.getId();
        String username = SecurityUtils.getUsername();
        long voteCount = this.voteService.getUserMomentVoteCount(dbMomentId, username);
        if (voteCount > 0) {
            throw new ServiceException("投票失败，已对当前动态投票。");
        }

        String optionCode = bo.getOptionCode();
        BbsMomentVoteOption dbOption = this.voteOptionService.getVoteOption(dbMomentId, optionCode);
        if (Objects.isNull(dbOption)) {
            throw new ServiceException("投票失败，未查询到当前投票选项数据。");
        }

        // 添加投票记录
        this.voteService.addMomentVoteCount(dbMomentId, username, optionCode);

        // 更新投票选项投票人数
        BbsMomentVoteOption updOption = new BbsMomentVoteOption();
        updOption.setId(dbOption.getId());
        updOption.setVoteCount(dbOption.getVoteCount() + 1);
        this.voteOptionService.updateById(updOption);

        // 更新动态投票人数
        this.baseMapper.incrMomentVoteCount(dbMomentId, 1);

        // 发布用户操作事件
        OperateEvent operateEvent = new OperateEvent(username, OperateType.TPDT);
        this.eventPublisher.publishEvent(operateEvent);
    }


    /**
     * 评论动态
     *
     * @param bo 动态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userCommentMoment(CommentMomentBO bo) {
        BbsMoment dbMoment = this.getById(bo.getMomentId());
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("评论失败，未查询到当前动态数据。");
        }
        if (EnableStatus.QY.getStatus() != dbMoment.getIsEnableComment()) {
            throw new ServiceException("评论失败，当前动态未开启评论。");
        }

        // 检查评论内容
        String content = bo.getContent();
        FoundWord fw = SensitiveWordUtils.getFoundFirstSensitive(content);
        if (Objects.nonNull(fw)) {
            String msg = String.format("评论失败，当前评论内容存在敏感词【%s】。", fw.getFoundWord());
            throw new ServiceException(msg);
        }

        // 回复评论
        BbsMomentComment dbParentComment = null;
        if (Objects.nonNull(bo.getParentId())) {
            dbParentComment = this.commentService.getById(bo.getParentId());
            if (Objects.isNull(dbParentComment)) {
                throw new ServiceException("回复失败，未查询到回复的评论数据");
            }
        }

        // 添加动态评论
        Long dbMomentId = dbMoment.getId();
        SysUser dbUser = SecurityUtils.getLoginUser().getUser();
        BbsMomentComment newComment = new BbsMomentComment();
        newComment.setMomentId(dbMomentId);
        newComment.setUserName(dbUser.getUserName());
        newComment.setNickName(dbUser.getNickName());
        newComment.setContent(content);
        newComment.setStatus(CommentStatus.ZC.getStatus());
        newComment.setIsFeatured(0);
        this.commentService.save(newComment);

        // 更新动态评论
        BbsMomentComment updComment = new BbsMomentComment();
        updComment.setId(newComment.getId());
        if (Objects.nonNull(dbParentComment)) {
            updComment.setParentId(dbParentComment.getId());
            updComment.setReplyNickName(dbParentComment.getNickName());
            updComment.setAncestorPath(dbParentComment.getAncestorPath() + "," + newComment.getId());
        } else {
            updComment.setAncestorPath(String.valueOf(newComment.getId()));
        }
        this.commentService.updateById(updComment);

        // 更新动态评论人数
        this.baseMapper.incrMomentCommentCount(dbMomentId, 1);

        // 发布用户操作事件
        OperateEvent operateEvent = new OperateEvent(dbUser.getUserName(), OperateType.TPDT);
        this.eventPublisher.publishEvent(operateEvent);
    }


    /**
     * 删除评论
     *
     * @param commentId 评论Id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userDeleteComment(Long commentId) {
        BbsMomentComment dbComment = this.commentService.getById(commentId);
        if (Objects.isNull(dbComment)) {
            throw new ServiceException("删除失败，未查询到当前动态评论数据。");
        }

        String userName = SecurityUtils.getUsername();
        if (!dbComment.getUserName().equals(userName)) {
            throw new ServiceException("删除失败，没有对当前动态评论的操作权限。");
        }

        this.commentService.removeById(dbComment.getId());

        // 更新动态评论人数
        this.baseMapper.incrMomentCommentCount(dbComment.getMomentId(), 1);
    }


    /**
     * 精选评论
     *
     * @param bo 评论
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void userFeaturedComment(FeaturedCommentBO bo) {
        BbsMoment dbMoment = this.getById(bo.getMomentId());
        if (Objects.isNull(dbMoment)) {
            throw new ServiceException("操作失败，未查询到当前动态数据。");
        }

        String userName = SecurityUtils.getUsername();
        if (!dbMoment.getUserName().equals(userName)) {
            throw new ServiceException("操作失败，没有对当前动态评论的操作权限。");
        }
        if (EnableStatus.QY.getStatus() != dbMoment.getIsEnableFeaturedComment()) {
            throw new ServiceException("操作失败，当前动态未开启评论精选。");
        }

        // 动态评论
        BbsMomentComment dbComment = this.commentService.getOne(
            Wrappers.lambdaQuery(BbsMomentComment.class)
                .eq(BbsMomentComment::getMomentId, dbMoment.getId()).eq(BbsMomentComment::getId, bo.getCommentId())
        );
        if (Objects.isNull(dbComment)) {
            throw new ServiceException("操作失败，未查询到当前动态评论数据。");
        }

        BbsMomentComment updComment = new BbsMomentComment();
        updComment.setId(dbComment.getId());
        updComment.setIsFeatured(bo.getFeaturedStatus());
        this.commentService.updateById(updComment);
    }


    /**
     * 构建动态实体
     *
     * @param bo 动态
     * @return 动态实体
     */
    private BbsMoment buildMoment(CreateMomentBO bo) {
        // 动态类型
        MomentType momentType = MomentType.getMomentType(bo.getType());
        if (Objects.isNull(momentType)) {
            throw new ServiceException("发布失败，当前动态类型错误。");
        }

        // 动态附件
        BbsMoment newMoment = new BbsMoment();
        List<CreateMomentBO.Attachment> attachments = bo.getAttachments();
        String content = bo.getContent();

        // 动态类型
        if (MomentType.TEXT == momentType || MomentType.IMAGE == momentType || MomentType.VIDEO == momentType) {
            if (MomentType.TEXT == momentType) {
                if (StrUtil.isBlank(content)) {
                    throw new ServiceException("发布失败，当前动态内容不能为空。");
                }
            }
            if (MomentType.IMAGE == momentType || MomentType.VIDEO == momentType) {
                if (CollUtil.isEmpty(attachments)) {
                    throw new ServiceException("发布失败，当前动态附件不能为空。");
                }
            }
            if (StrUtil.isNotBlank(content)) {
                FoundWord fw = SensitiveWordUtils.getFoundFirstSensitive(content);
                if (Objects.nonNull(fw)) {
                    String msg = String.format("发布失败，当前动态内容存在敏感词【%s】。", fw.getFoundWord());
                    throw new ServiceException(msg);
                } else {
                    newMoment.setContent(content);
                }
            }
        }
        else if (MomentType.URL == momentType) {
            String linkUrl = bo.getLinkUrl();
            if (StrUtil.isBlank(linkUrl)) {
                throw new ServiceException("发布失败，当前动态链接不能为空。");
            } else {
                newMoment.setLinkUrl(linkUrl);
            }
        }

        newMoment.setType(momentType.name());
        newMoment.setIsEnableComment(bo.getIsEnableComment());
        newMoment.setIsEnableFeaturedComment(bo.getIsEnableFeaturedComment());

        // 动态主题
        List<String> topicNames = bo.getTopicNames();
        List<String> topicIds = bo.getTopicIds();
        if (CollUtil.isNotEmpty(topicNames) && CollUtil.isNotEmpty(topicIds)) {
            newMoment.setTopicIds(String.join(",", topicIds));
            newMoment.setTopicNames(String.join(",", topicNames));
        }

        // 动态投票
        Integer isEnableVote = bo.getIsEnableVote();
        newMoment.setIsEnableVote(isEnableVote);
        if (EnableStatus.QY.getStatus() == isEnableVote) {
            VoteOptionType voteType = VoteOptionType.getVoteOptionType(bo.getVoteOptionType());
            if (Objects.isNull(voteType)) {
                throw new ServiceException("发布失败，当前动态投票选项类型错误。");
            } else {
                newMoment.setVoteOptionType(voteType.name());
                newMoment.setVoteTitle(bo.getVoteTitle());
            }

            List<CreateMomentBO.VoteOption> options = bo.getVoteOptions();
            if (CollUtil.isEmpty(options)) {
                throw new ServiceException("发布失败，当前动态投票选项不能为空。");
            } else {
                for (CreateMomentBO.VoteOption option : options) {
                    long optionCount = options.stream()
                        .filter(o -> o.getName().equals(option.getName())).count();
                    if (optionCount > 1) {
                        throw new ServiceException("发布失败，当前动态投票选项重复。");
                    }
                }
            }
        }
        return newMoment;
    }


    /**
     * 填充动态附件、投票选项和评论
     *
     * @param moments 动态
     * @param userName 用户名
     */
    private void fillMoment(List<BbsMomentListVO> moments, String userName) {
        log.info("=> 填充动态附件、投票选项和评论");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        List<Long> momentIds = moments.stream().map(BbsMomentListVO::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(momentIds)) {
            return;
        }

        try {
            // 查询动态附件
            CompletableFuture<Map<Long, List<BbsMomentAttachment>>> af = CompletableFuture.supplyAsync(() -> {
                List<BbsMomentAttachment> atts = this.attachmentsService.getAttachments(momentIds);
                return atts.stream().collect(Collectors.groupingBy(BbsMomentAttachment::getMomentId));
            }).exceptionally(e -> {
                log.error("=> 查询动态附件失败：", e);
                return new HashMap<>(0);
            });

            // 查询动态投票选项
            CompletableFuture<Map<Long, List<BbsMomentVoteOption>>> of = CompletableFuture.supplyAsync(() -> {
                List<BbsMomentVoteOption> opts = this.voteOptionService.getVoteOptions(momentIds);
                return opts.stream().collect(Collectors.groupingBy(BbsMomentVoteOption::getMomentId));
            }).exceptionally(e -> {
                log.error("=> 查询动态投票选项失败：", e);
                return new HashMap<>(0);
            });

            // 查询动态评论
            CompletableFuture<Map<Long, List<BbsMomentComment>>> cf = CompletableFuture.supplyAsync(() -> {
                List<BbsMomentComment> opts = this.commentService.getMomentLimitComments(momentIds, userName);
                return opts.stream().collect(Collectors.groupingBy(BbsMomentComment::getMomentId));
            }).exceptionally(e -> {
                log.error("=> 查询动态评论失败：", e);
                return new HashMap<>(0);
            });

            // 等待全部任务处理完毕
            CompletableFuture.allOf(af, of, cf).join();
            Map<Long, List<BbsMomentAttachment>> attsMap = af.join();
            Map<Long, List<BbsMomentVoteOption>> optsMap = of.join();
            Map<Long, List<BbsMomentComment>> comsMap = cf.join();

            for (BbsMomentListVO moment : moments) {
                // 填充动态附件
                List<BbsMomentAttachment> as = attsMap.get(moment.getId());
                if (CollUtil.isNotEmpty(as)) {
                    List<BbsAttachmentVO> avs = new ArrayList<>(as.size());
                    for (BbsMomentAttachment a : as) {
                        BbsAttachmentVO av = new BbsAttachmentVO();
                        av.setId(a.getId());
                        av.setName(a.getName());
                        av.setUrl(a.getUrl());
                        avs.add(av);
                    }
                    moment.setAttachments(avs);
                }

                // 填充动态投票选项
                List<BbsMomentVoteOption> os = optsMap.get(moment.getId());
                if (CollUtil.isNotEmpty(os)) {
                    List<BbsVoteOptionVO> ovs = new ArrayList<>(os.size());
                    for (BbsMomentVoteOption o : os) {
                        BbsVoteOptionVO ov = new BbsVoteOptionVO();
                        ov.setId(o.getId());
                        ov.setType(o.getType());
                        ov.setName(o.getName());
                        ov.setCode(o.getCode());
                        ov.setImageUrl(o.getImageUrl());
                        ov.setSort(o.getSort());
                        ov.setVoteCount(o.getVoteCount());
                        ovs.add(ov);
                    }
                    moment.setVoteOptions(ovs);
                }

                // 填充动态评论
                List<BbsMomentComment> cs = comsMap.get(moment.getId());
                if (CollUtil.isNotEmpty(cs)) {
                    List<BbsCommentVO> cvs = new ArrayList<>(cs.size());
                    for (BbsMomentComment c : cs) {
                        BbsCommentVO cv = new BbsCommentVO();
                        cv.setId(c.getId());
                        cv.setCreateTime(c.getCreateTime());
                        cv.setNickName(c.getNickName());
                        cv.setUserName(c.getUserName());
                        cv.setContent(c.getContent());
                        cv.setIsSelf(c.getIsSelf());
                        cvs.add(cv);
                    }
                    moment.setComments(cvs);
                }
            }

        } catch (Exception e) {
            log.error("=> 填充动态附件和投票选项失败：", e);
        }

        stopWatch.stop();
        long millis = stopWatch.getLastTaskTimeMillis();
        log.info("=> 填充动态附件、投票选项和评论完毕，耗时：{} 毫秒", millis);
    }

}
