package org.lym.pom.service.impl;

import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import org.lym.pom.notify.event.CommentAddEvent;
import org.lym.pom.notify.event.CommentRemoveEvent;
import org.lym.pom.notify.publisher.EventPublisher;
import org.lym.pom.repository.mongo.Comment;
import org.lym.pom.repository.mongo.Comment.Status;
import org.lym.pom.service.CommentService;
import org.shoulder.core.concurrent.Threads;
import org.shoulder.core.dto.request.BasePageQuery;
import org.shoulder.core.dto.response.PageResult;
import org.shoulder.core.exception.CommonErrorCodeEnum;
import org.shoulder.core.util.AssertUtils;
import org.shoulder.core.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 评论/赞 服务
 *
 * @author lym
 */
@Service
public class CommentServiceImpl implements CommentService {

    @Autowired
    private EventPublisher eventPublisher;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public Comment addComment(Comment comment) {
        // todo 进阶功能 检查被评论的是否开启禁止评论、不能（短时间）重复刷内容相同评论等、校验格式、评论内容
        //        comment.setId("todo");
        AssertUtils.notNull(comment.getRelatedId(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        AssertUtils.notNull(comment.getType(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        Comment parentComment = null;
        if (comment.getParentId() != null) {
            // 评论的评论 or 孙级回复, 找到父级评论
             parentComment = queryCommentDetail(comment.getParentId());
            // 肯定存在，毕竟删是软删除
            AssertUtils.notNull(parentComment, CommonErrorCodeEnum.ILLEGAL_PARAM);

            comment.setRootCommentId(parentComment.getRootCommentId());
            comment.setRootCommentUserId(parentComment.getRootCommentUserId());
            comment.setRelatedUserId(parentComment.getRelatedUserId());
            comment.setRelatedType(parentComment.getRelatedType());
            List<String> mentionedUserIdList = Optional.ofNullable(comment.getMentionedUserIdList()).orElse(Lists.newLinkedList());
            mentionedUserIdList.add(0, parentComment.getCreator());
            comment.setMentionedUserIdList(mentionedUserIdList);
        } else {
            comment.setParentId(Comment.EMPTY_PARENT_ID);
        }

        AssertUtils.notNull(comment.getRelatedUserId(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        AssertUtils.notNull(comment.getRelatedType(), CommonErrorCodeEnum.ILLEGAL_PARAM);

        //comment.setType(Comment.Type.COMMENT);
        comment.setStatus(Comment.Status.NORMAL);
        comment.setScore(10.0);
        comment.setCreateTime(System.currentTimeMillis());
        comment.setUpdateTime(System.currentTimeMillis());
        comment = mongoTemplate.save(comment);

        eventPublisher.publish(new CommentAddEvent(comment, parentComment));
        return comment;
    }

    /**
     * 删除评论，必须根据 id 删除，避免多条评论都删了 todo 是否删除下级评论？
     *
     * @param comment 评论信息 id
     */
    @Override
    public void removeComment(Comment comment) {
        // 调用方校验权限：是自己还在可删的评论
        //AssertUtils.equals(Comment.Type.COMMENT, comment.getType(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        AssertUtils.notEmpty(comment.getId(), CommonErrorCodeEnum.ILLEGAL_PARAM);

        Comment toDeleteComment = queryCommentDetail(comment.getId());
        if (toDeleteComment == null) {
            // 不存在说明已经删除
            return;
        }

        Query query = Query.query(Criteria.where("id").is(comment.getId()));
        Update update = new Update()
                .set("status", Status.DELETED)
                .set("updateTime", System.currentTimeMillis());

        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Comment.class);

        if (updateResult.wasAcknowledged()) {
            if (updateResult.getMatchedCount() == 0) {
                // 不存在说明已经删除
                return;
            }
            if (updateResult.getModifiedCount() > 0) {
                eventPublisher.publish(new CommentRemoveEvent(toDeleteComment));
            }
        } else {
            // 未响应
            boolean exists = mongoTemplate.exists(query, Comment.class);
            if (exists) {
                throw new RuntimeException("delete FAIL, please retry.");
            } else {
                // 可能本次删了，也可能其他请求删了，仍然发通知
                eventPublisher.publish(new CommentRemoveEvent(toDeleteComment));
            }
        }
    }

    /**
     * 分页查询某个 relatedId && relatedType 下的直接评论
     *
     * @param pageQuery 查询条件
     * @return result
     */
    @Override
    public PageResult<Comment> pageQueryDirectComments(BasePageQuery<Comment> pageQuery) {
        // 根据 relatedId、relatedType、type 进行分页查询
        Criteria queryCriteria = Criteria.where("relatedId").is(pageQuery.getCondition().getRelatedId())
                .and("relatedType").is(pageQuery.getCondition().getRelatedType())
                .and("rootCommentId").is(null)
                .and("status").is(Status.NORMAL);
        return pageQuery(pageQuery, queryCriteria);
    }

    private PageResult<Comment> pageQuery(BasePageQuery<Comment> pageQuery, Criteria queryCriteria) {
        Query query = Query.query(queryCriteria)
                .skip((long) (pageQuery.getPageNo() - 1) * pageQuery.getPageSize())
                .limit(pageQuery.getPageSize())
                .with(Sort.by(
                        //Order.asc("score"),
                        //Order.desc("likeNumber"),
                        //Order.asc("dislikeNumber"),
                        Order.asc("createTime")
                ));

        // 分页查询
        List<Comment> comments = mongoTemplate.find(query, Comment.class);

        // 查询总数 todo 并行
        long total = mongoTemplate.count(new Query(queryCriteria), Comment.class);

        // 返回分页结果
        return PageResult.build(comments, pageQuery.getPageNo(), pageQuery.getPageSize(), total);
    }

    /**
     * 分页查询某个评论下的子评论（ rootCommentId && type=Comment ）
     *
     * @param pageQuery 查询条件
     * @return result
     */
    @Override
    public PageResult<Comment> pageQuerySubComments(BasePageQuery<Comment> pageQuery) {
        AssertUtils.notNull(pageQuery.getCondition().getRootCommentId(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        AssertUtils.notNull(pageQuery.getCondition().getRelatedId(), CommonErrorCodeEnum.ILLEGAL_PARAM);
        // 根据 relatedId、relatedType、type 进行分页查询
        Criteria queryCriteria = Criteria.where("rootCommentId").is(pageQuery.getCondition().getRootCommentId())
                .and("relatedId").is(pageQuery.getCondition().getRelatedId())
                .and("status").is(Status.NORMAL);
        return pageQuery(pageQuery, queryCriteria);
    }

    /**
     * 分页查询某个评论下的子评论（ rootCommentId && type=Comment ）
     *
     * @param pageQuery 查询条件
     * @return result
     */
    @Override
    public PageResult<Comment> pageQueryDirectCommentsWithSubComments(BasePageQuery<Comment> pageQuery) {
        PageResult<Comment> result = pageQueryDirectComments(pageQuery);
        if(result.getList() == null) {
            return result;
        }
        try {
            Threads.executeAndWait(
                    result.getList().stream().map(
                            comment -> (Runnable) () -> {
                                BasePageQuery<Comment> subPageQuery = new BasePageQuery<>();
                                Comment subQueryCondition = new Comment();
                                subQueryCondition.setRelatedId(comment.getRelatedId());
                                subQueryCondition.setRelatedType(comment.getRelatedType());
                                subQueryCondition.setRootCommentId(comment.getId());
                                subPageQuery.setCondition(subQueryCondition);
                                subPageQuery.setPageNo(1);
                                subPageQuery.setPageSize(10);
                                PageResult<Comment> subComments = pageQuerySubComments(subPageQuery);
                                comment.setSubComments(subComments);
                            }
                    ).collect(Collectors.toList()), Duration.ofSeconds(2));
        } catch (InterruptedException ignore) {
            return result;
        }
        result.getList().forEach(comment -> AssertUtils.notNull(comment.getSubComments(), CommonErrorCodeEnum.CODING));
        return result;
    }

    /**
     * 根据 id 查询单条评论
     *
     * @param comment 评论（id）
     * @return 评论详情
     */
    @Override
    public Comment queryCommentDetail(String commentId) {
        AssertUtils.notBlank(commentId, CommonErrorCodeEnum.ILLEGAL_PARAM);
        Query query = Query.query(Criteria.where("id").is(commentId));
        return mongoTemplate.findOne(query, Comment.class);
    }

    @Override
    public long countCommentNumber(String relatedId, String relatedType, String rootCommentId) {
        Criteria criteria = Criteria.where("relatedId").is(relatedId)
                .and("relatedType").is(relatedType);
        if (StringUtils.isNotBlank(rootCommentId)) {
            criteria = criteria.and("rootCommentId").is(rootCommentId);
        }
        //if(StringUtils.isNotBlank(type)) {
        //    criteria = criteria.and("type").is(type);
        //}
        Query query = Query.query(criteria);
        return mongoTemplate.count(query, Comment.class);
    }

}
