package org.agt.module.license.service.comment;

import jakarta.annotation.Resource;
import org.agt.framework.common.util.object.BeanUtils;
import org.agt.framework.web.core.util.WebFrameworkUtils;
import org.agt.module.license.controller.admin.comment.vo.CommentListReqVO;
import org.agt.module.license.controller.admin.comment.vo.CommentSaveReqVO;
import org.agt.module.license.controller.admin.comment.vo.CommentTreeRespVO;
import org.agt.module.license.dal.dataobject.comment.CommentDO;
import org.agt.module.license.dal.dataobject.project.ProjectDO;
import org.agt.module.license.dal.mysql.comment.CommentMapper;
import org.agt.module.license.dal.mysql.project.ProjectMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.agt.framework.common.exception.util.ServiceExceptionUtil.exception;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_CONTENT_DUPLICATE;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_EXITS_CHILDREN;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_NOT_EXISTS;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_PARENT_ERROR;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_PARENT_IS_CHILD;
import static org.agt.module.license.enums.ErrorCodeConstants.COMMENT_PARENT_NOT_EXITS;

/**
 * 评论 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
public class CommentServiceImpl implements CommentService {

    @Resource
    private CommentMapper commentMapper;

    @Resource
    private ProjectMapper projectMapper;

    @Override
    public Long createComment(CommentSaveReqVO createReqVO) {
        createReqVO.setUserId(WebFrameworkUtils.getLoginUserId());
        if (createReqVO.getParentId() == null) {
            createReqVO.setParentId(0L);
        }
        // 校验父ID的有效性
        validateParentComment(null, createReqVO.getParentId());
        // 校验内容的唯一性
        validateCommentContentUnique(null, createReqVO.getParentId(), createReqVO.getContent());

        // 插入
        CommentDO comment = BeanUtils.toBean(createReqVO, CommentDO.class);
        commentMapper.insert(comment);

        projectMapper.updateById(new ProjectDO().setId(createReqVO.getProjectId()));
        // 返回
        return comment.getId();
    }

    @Override
    public void updateComment(CommentSaveReqVO updateReqVO) {
        // 校验存在
        validateCommentExists(updateReqVO.getId());
        // 校验父ID的有效性
        validateParentComment(updateReqVO.getId(), updateReqVO.getParentId());
        // 校验内容的唯一性
        validateCommentContentUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getContent());

        // 更新
        CommentDO updateObj = BeanUtils.toBean(updateReqVO, CommentDO.class);
        commentMapper.updateById(updateObj);

        projectMapper.updateById(new ProjectDO().setId(updateReqVO.getProjectId()));
    }

    @Override
    public void deleteComment(Long id) {
        // 校验存在
        validateCommentExists(id);
        // 校验是否有子评论
        if (commentMapper.selectCountByParentId(id) > 0) {
            throw exception(COMMENT_EXITS_CHILDREN);
        }
        // 删除
        commentMapper.deleteById(id);
    }

    private void validateCommentExists(Long id) {
        if (commentMapper.selectById(id) == null) {
            throw exception(COMMENT_NOT_EXISTS);
        }
    }

    private void validateParentComment(Long id, Long parentId) {
        if (parentId == null || CommentDO.PARENT_ID_ROOT.equals(parentId)) {
            return;
        }
        // 1. 不能设置自己为父评论
        if (Objects.equals(id, parentId)) {
            throw exception(COMMENT_PARENT_ERROR);
        }
        // 2. 父评论不存在
        CommentDO parentComment = commentMapper.selectById(parentId);
        if (parentComment == null) {
            throw exception(COMMENT_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父评论，如果父评论是自己的子评论，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentComment.getParentId();
            if (Objects.equals(id, parentId)) {
                throw exception(COMMENT_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父评论
            if (parentId == null || CommentDO.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentComment = commentMapper.selectById(parentId);
            if (parentComment == null) {
                break;
            }
        }
    }

    private void validateCommentContentUnique(Long id, Long parentId, String content) {
        CommentDO comment = commentMapper.selectByParentIdAndContent(parentId, content);
        if (comment == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的评论
        if (id == null) {
            throw exception(COMMENT_CONTENT_DUPLICATE);
        }
        if (!Objects.equals(comment.getId(), id)) {
            throw exception(COMMENT_CONTENT_DUPLICATE);
        }
    }

    @Override
    public CommentDO getComment(Long id) {
        return commentMapper.selectById(id);
    }

    @Override
    public List<CommentDO> getCommentList(CommentListReqVO listReqVO) {
        return commentMapper.selectList(listReqVO);
    }

    @Override
    public List<CommentTreeRespVO> getCommentTree(Long projectId, Boolean sort) {
        List<CommentTreeRespVO> comments = commentMapper.getCommentList(projectId);
        List<CommentTreeRespVO> commentTree = comments.stream().filter(c -> c.getParentId() == 0)
                .peek(c -> {
                    c.setDepth(1);
                    c.setChildren(getChildren(c, comments, 1));
                })
                .sorted(sort != null && sort ? Comparator.comparing(CommentTreeRespVO::getUpdateTime): Comparator.comparing(CommentTreeRespVO::getUpdateTime).reversed())
                .toList();
        return commentTree.stream().filter(c -> c.getParentId() == 0)
                .peek(c -> {
                    List<CommentTreeRespVO> childrenMerge = c.getChildren();
                    addChildrenMerge(c, comments, childrenMerge);
                    childrenMerge = childrenMerge.stream()
                            .sorted(sort != null && sort ? Comparator.comparing(CommentTreeRespVO::getUpdateTime): Comparator.comparing(CommentTreeRespVO::getUpdateTime).reversed())
                            .collect(Collectors.toList());
                    c.setChildren(childrenMerge);
                }).toList();
    }

    @Override
    public List<CommentTreeRespVO> getLatestCommentList() {
        return commentMapper.getLatestCommentList();
    }


    public static List<CommentTreeRespVO> getChildren(CommentTreeRespVO root, List<CommentTreeRespVO> all, int i) {
        return all.stream().filter(c -> Objects.equals(c.getParentId(), root.getId()))
                .peek(c -> {
                    c.setDepth(i+1);
                    c.setChildren(getChildren(c, all, i+1));
                }).collect(Collectors.toList());
    }

    public static void addChildrenMerge(CommentTreeRespVO root, List<CommentTreeRespVO> all, List<CommentTreeRespVO> children) {
        all.stream().filter(c -> Objects.equals(c.getParentId(), root.getId()))
                .forEach(c -> {
                    children.addAll(c.getChildren());
                    addChildrenMerge(c, all, children);
                });
    }
}