package com.xxfc.platform.im.biz;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.github.wxiaoqi.security.admin.entity.AppUserLogin;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.admin.feign.dto.AppUserDTO;
import com.github.wxiaoqi.security.admin.vo.ImiVo;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.util.RandomUtil;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.xxfc.platform.im.dto.CommentVo;
import com.xxfc.platform.im.dto.MsgQueryDto;
import com.xxfc.platform.im.dto.PraiseVo;
import com.xxfc.platform.im.dto.QuestionParamDto;
import com.xxfc.platform.im.model.*;
import com.xxfc.platform.im.vo.MsgVo;
import com.xxfc.platform.universal.constant.DictionaryKey;
import com.xxfc.platform.universal.entity.Dictionary;
import com.xxfc.platform.universal.feign.ThirdFeign;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
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.util.*;
import java.util.stream.Collectors;

import static com.xxfc.platform.universal.constant.DictionaryKey.IM_TYPE;

@Service
@Slf4j
public class MsgBiz {

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    UserBiz userBiz;

    @Autowired
    ImQuestionBiz imQuestionBiz;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    UserFeign userFeign;
    /**
     * 获取消息列表
     *
     * @return
     * @throws Exception
     */
    public ObjectRestResponse getMsgList(Integer page, Integer limit, Integer type) throws Exception {
        //获取所有朋友圈
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        Integer userId = null;
        if (appUserDTO != null) {
            userId = appUserDTO.getImUserid();
        }
        page = page == null ? 1 : page;
        limit = limit == null ? 10 : limit;
        Pageable pageable = PageRequest.of(--page, limit);
        Query query = null;
        List<Msg> msgList = null;
        if (type != null) {
            query = new Query(Criteria.where("body.type").is(type).and("visible").is(1));
            int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
            query.with(pageable);
            query.with(new Sort(Sort.Direction.DESC, "time"));
            msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);
            PageInfo<MsgVo> goodPageInfo = new PageInfo<>(replaceMsgResult(msgList));
            goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
            return ObjectRestResponse.succ(goodPageInfo);
        } else {
            List<Integer> ids = new ArrayList<>();
            ids.add(2);
            ids.add(4);
            query = new Query(Criteria.where("body.type").in(ids).and("visible").is(1));
            int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
            query.with(pageable);
            query.with(new Sort(Sort.Direction.DESC, "time"));
            msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);

            PageInfo<MsgVo> goodPageInfo = new PageInfo<>(replaceMsgResult(msgList));
            goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
            return ObjectRestResponse.succ(goodPageInfo);
        }

    }

    public ObjectRestResponse getHotMsgList(Integer page, Integer limit) {
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        Integer userId = null;
        if (appUserDTO != null) {
            userId = appUserDTO.getImUserid();
        }
        page = page == null ? 1 : page;
        limit = limit == null ? 10 : limit;
        Pageable pageable = PageRequest.of(--page, limit);
        List<Integer> ids = new ArrayList<>();
        ids.add(2);
        ids.add(4);
        Query query = new Query(Criteria.where("body.type").in(ids).and("count.praise").gt(getNumber()).and("visible").is(1));
        int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
        query.with(pageable);
        query.with(new Sort(Sort.Direction.DESC, "count.praise"));
        List<Msg> msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);
        PageInfo<MsgVo> goodPageInfo = new PageInfo<>(replaceMsgResult(msgList));
        goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
        return ObjectRestResponse.succ(goodPageInfo);
    }

    public ObjectRestResponse getHotMsgListWithoutOne(String messageId, Integer number) {
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        Integer userId = null;
        if (appUserDTO != null) {
            userId = appUserDTO.getImUserid();
        }
        number = number == null ? 10 : number;
        Query query = new Query(Criteria.where("body.type").is(4).and("count.praise").gt(getNumber()).and("visible").is(1));
        if (StringUtils.isNotBlank(messageId)) {
            query.addCriteria(Criteria.where("_id").ne(new ObjectId(messageId)));
        }
        query.with(new Sort(Sort.Direction.DESC, "count.praise"));
        List<Msg> msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);
        List<MsgVo> msgVoList = replaceMsgResult(msgList);
        Set<MsgVo> resultList = new HashSet<>();
        if(CollectionUtils.isNotEmpty(msgVoList)) {
            if(number >= msgVoList.size()) {
                return ObjectRestResponse.succ(msgVoList);
            }
            Set<Integer> set = new HashSet<>();
            RandomUtil.randomSet(msgVoList.size(), number, set, number);
            for(Integer i : set) {
                resultList.add(msgVoList.get(i));
            }
        }
        return ObjectRestResponse.succ(resultList);
    }

    public ObjectRestResponse get(String id) {
        //获取所有朋友圈
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        Integer userId = null;
        if (appUserDTO != null) {
            userId = appUserDTO.getImUserid();
        }
        if (id == null) {
            return ObjectRestResponse.paramIsEmpty();
        }
        Query query = new Query(Criteria.where("id").is(new ObjectId(id)).and("visible").is(1));
        List<Msg> msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);
        List<MsgVo> msgVoList = replaceMsgResult(msgList);
        if (msgVoList.size() > 0) {
            return ObjectRestResponse.succ(msgVoList.get(0));
        }
        return ObjectRestResponse.createFailedResult(ResultCode.IM_MSG_NOT_EXIST_CODE, ResultCode.getMsg(ResultCode.IM_MSG_NOT_EXIST_CODE));
    }

    public ObjectRestResponse getMsgListByUserId(Integer page, Integer limit, Integer type) {
        //获取所有朋友圈
        page = page == null ? 1 : page;
        limit = limit == null ? 10 : limit;
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        Integer userId = null;
        if (appUserDTO != null) {
            userId = appUserDTO.getImUserid();
        } else {
            return ObjectRestResponse.createFailedResult(ResultCode.RSTOKEN_EXPIRED_CODE, "token失效");
        }
        Pageable pageable = PageRequest.of(--page, limit);
        List<Integer> ids = new ArrayList<>();
        if (type != null) {
            ids.add(type);
        }
        if (type != null && type == 5) {
            QuestionParamDto questionParamDto = new QuestionParamDto();
            questionParamDto.setUserId(Long.parseLong(userId + ""));
            return imQuestionBiz.getList(questionParamDto);
        }
        Query query = new Query(Criteria.where("body.type").in(ids));
        query.addCriteria(Criteria.where("userId").is(userId));
        int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
        query.with(pageable);
        query.with(new Sort(Sort.Direction.DESC, "time"));
        List<Msg> msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), userId);
        List<MsgVo> msgVoList = replaceMsgResult(msgList);
        PageInfo<MsgVo> goodPageInfo = new PageInfo<>(msgVoList);
        goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
        return ObjectRestResponse.succ(PageDataVO.pageInfo(goodPageInfo));

    }

    /**
     * 删除消息
     *
     * @param ids id字符串，用逗号隔开
     * @return
     */
    public ObjectRestResponse deleteByList(String ids) {
        Integer userId = 0;
        AppUserDTO appUserDTO = userBiz.getUserInfo();
        if (appUserDTO == null) {
            return ObjectRestResponse.createFailedResult(ResultCode.RSTOKEN_EXPIRED_CODE, ResultCode.getMsg(ResultCode.RSTOKEN_EXPIRED_CODE));
        }
        if (StringUtils.isBlank(ids)) {
            return ObjectRestResponse.paramIsEmpty();
        }
        userId = appUserDTO.getImUserid();
        List<String> list = Arrays.asList(ids.split(","));
        Set<String> set = new HashSet<>();
        set.addAll(list);
        Query query = new Query(Criteria.where("id").in(set));
        query.addCriteria(Criteria.where("userId").is(userId));
        List<Msg> msgList = mongoTemplate.find(query, Msg.class, "s_msg");
        if (msgList.size() != set.size()) { //查询到的消息条数不等于查询的Id数
            return ObjectRestResponse.createFailedResult(ResultCode.IM_DELETE_FAIL_CODE, ResultCode.getMsg(ResultCode.IM_DELETE_FAIL_CODE));
        }
        DeleteResult deleteResult = mongoTemplate.remove(query, Msg.class, "s_msg");
        if (deleteResult != null && deleteResult.getDeletedCount() == set.size()) {
            return ObjectRestResponse.succ();
        }
        return ObjectRestResponse.createFailedResult(ResultCode.IM_DELETE_FAIL_CODE, ResultCode.getMsg(ResultCode.IM_DELETE_FAIL_CODE));
    }


    /**
     * 添加评论和点赞
     *
     * @param list
     * @param userId
     * @return
     */
    private List<Msg> fetchAndAttach(List<Msg> list, Integer userId) {
        Iterator<Msg> iterator = list.iterator();
        while (iterator.hasNext()) {
            Msg msg = iterator.next();
            Query query = new Query(Criteria.where("msgId").is(msg.getId()));
            query.with(new Sort(Sort.Direction.DESC, "time"));
            //添加点赞
            List<Praise> praise = mongoTemplate.find(query, Praise.class, "s_praise");
            msg.setPraises(replacePraiseResult(praise));
            if (userId != null) {
                msg.setIsPraise(this.exists(userId, msg.getId()) ? 1 : 0);
                msg.setIsCollect(this.existsCollect(userId, msg.getId()) ? 1 : 0);
            } else {
                msg.setIsPraise(0);
                msg.setIsCollect(0);
            }
            //添加评论

            List<Comment> comments = mongoTemplate.find(query, Comment.class, "s_comment");
            msg.setComments(replaceCommentResult(comments));
        }

        return list;
    }

    /**
     * 判断是否点赞
     *
     * @param userId
     * @param msgId
     * @return
     */
    public boolean exists(int userId, ObjectId msgId) {
        Query query = new Query(Criteria.where("msgId").is(msgId).and("userId").is(userId));
        List<Praise> praise = mongoTemplate.find(query, Praise.class, "s_praise");
        return praise.size() > 0 ? true : false;
    }

    /**
     * 判断是否评论
     *
     * @param userId
     * @param msgId
     * @return
     */
    public boolean existsCollect(int userId, ObjectId msgId) {
        Query query = new Query(Criteria.where("msgId").is(msgId).and("userId").is(userId));
        List<Comment> comments = mongoTemplate.find(query, Comment.class, "s_comment");
        return comments.size() > 0 ? true : false;
    }

    public List<MsgVo> replaceMsgResult(List<Msg> list) {
        List<MsgVo> msgVoList = new ArrayList<>();
        for (Msg msg : list) {
            MsgVo msgVo = new MsgVo();
            if(msg != null) {
                BeanUtil.copyProperties(msg, msgVo, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                //添加用户昵称和头像
                ImiVo imiVo = userBiz.getUserInfo(msg.getUserId());
                if (imiVo != null) {
                    msgVo.setNickname(imiVo.getNickname());
                    msgVo.setPicUrl(imiVo.getHeadimgurl());
                }
                //String address = AddressUtils.getMapaddress(msg.getLatitude().toString(), msg.getLongitude().toString());
                msgVo.setAddress(msg.getLocation());
                msgVo.setMsgId(msg.getId().toString());
                if(msg.getSource() == null) { //默认是2 兼容以前数据
                    msgVo.setSource(2);
                }
            }
            msgVoList.add(msgVo);
        }
        return msgVoList;
    }

    public List<PraiseVo> replacePraiseResult(List<Praise> list) {
        List<PraiseVo> praiseVoArrayList = new ArrayList<>();
        for (Praise praise : list) {
            PraiseVo praiseVo = new PraiseVo();
            if (praise != null) {
                BeanUtil.copyProperties(praise, praiseVo, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                //添加用户昵称和头像
                ImiVo imiVo = userBiz.getUserInfo(praise.getUserId());
                if (imiVo != null) {
                    praiseVo.setNickname(imiVo.getNickname());
                    praiseVo.setPicUrl(imiVo.getHeadimgurl());
                }
            }
            praiseVoArrayList.add(praiseVo);
        }
        return praiseVoArrayList;
    }

    public List<CommentVo> replaceCommentResult(List<Comment> list) {
        List<CommentVo> commentVoList = new ArrayList<>();
        for (Comment comment : list) {
            CommentVo commentVo = new CommentVo();
            if (comment != null) {
                BeanUtil.copyProperties(comment, commentVo, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
                //添加用户昵称和头像
                ImiVo imiVo = userBiz.getUserInfo(comment.getUserId());
                if (imiVo != null) {
                    commentVo.setNickname(imiVo.getNickname());
                    commentVo.setPicUrl(imiVo.getHeadimgurl());
                }
            }
            commentVoList.add(commentVo);
        }
        return commentVoList;
    }

    public Integer getNumber() {
        Integer number = 0;
            Dictionary dictionary =  thirdFeign.findDictionaryByTypeAndCode(IM_TYPE, DictionaryKey.MSG_LIMIT_NUMBER);
            if (dictionary != null && StringUtils.isNotBlank(dictionary.getDetail())) {
                log.info("查询IM限制数为字典表的结果为： dictionary = {}", dictionary.toString());
                number = Integer.parseInt(dictionary.getDetail());
            }
        log.info("IM消息数限制为： number = {}", number);
        return number;
    }


    /**
     * 后台管理接口
     */

    public ObjectRestResponse getMsgList(MsgQueryDto msgQueryDto){
        //获取所有朋友圈
        Integer page = msgQueryDto.getPage() == null ? 1 : msgQueryDto.getPage();
        Integer limit = msgQueryDto.getLimit() == null ? 10 : msgQueryDto.getLimit();
        Pageable pageable = PageRequest.of(--page, limit);
        Query query = null;
        List<Msg> msgList = null;
        if (msgQueryDto.getType() != null) {
            query = new Query(Criteria.where("body.type").is(msgQueryDto.getType()));
            //评论数
            if (msgQueryDto.getComment() != null) {
                query.addCriteria(Criteria.where("count.comment").gte(msgQueryDto.getComment()));
            }
            //点赞数
            if (msgQueryDto.getPraise() != null) {
                query.addCriteria(Criteria.where("count.praise").gte(msgQueryDto.getPraise()));
            }
            //来源
            if (msgQueryDto.getSource() != null) {
                if (msgQueryDto.getSource() == 1) {
                    query.addCriteria(Criteria.where("source").is(msgQueryDto.getSource()));
                } else {
                    query.addCriteria(Criteria.where("source").is(2));
                }
            }
            //下单时间
            if (msgQueryDto.getStartTime() != null ) {
                if (msgQueryDto.getEndTime() == null) {
                    query.addCriteria(Criteria.where("body.time").gte(msgQueryDto.getStartTime()).lte(System.currentTimeMillis()));
                } else {
                    query.addCriteria(Criteria.where("body.time").gte(msgQueryDto.getStartTime()).lte(msgQueryDto.getEndTime()));
                }
            } else {
                if (msgQueryDto.getEndTime() != null) {
                    query.addCriteria(Criteria.where("body.time").gte(57600000).lte(msgQueryDto.getEndTime()));
                }
            }
            //用户名
            if (StringUtils.isNotBlank(msgQueryDto.getUsername())) {
                AppUserLogin appUserLogin = userFeign.one(msgQueryDto.getUsername());
                if (appUserLogin != null) {
                    query.addCriteria(Criteria.where("userId").is(appUserLogin.getImUserid()));
                }
            }
            int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
            query.with(pageable);
            query.with(new Sort(Sort.Direction.DESC, "time"));
            msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), null);
            PageInfo<MsgVo> goodPageInfo = new PageInfo<>(replaceMsgResult(msgList));
            goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
            goodPageInfo.setTotal(totalSize);
            return ObjectRestResponse.succ(goodPageInfo);
        } else {
            List<Integer> ids = new ArrayList<>();
            ids.add(2);
            ids.add(4);
            query = new Query(Criteria.where("body.type").in(ids));
            int totalSize = mongoTemplate.find(query, Msg.class, "s_msg").size();
            query.with(pageable);
            query.with(new Sort(Sort.Direction.DESC, "time"));
            msgList = fetchAndAttach(mongoTemplate.find(query, Msg.class, "s_msg"), null);

            PageInfo<MsgVo> goodPageInfo = new PageInfo<>(replaceMsgResult(msgList));
            goodPageInfo.setPageSize(totalSize % limit == 0 ? totalSize / limit : totalSize / limit + 1);
            return ObjectRestResponse.succ(goodPageInfo);
        }

    }


    /**
     * 新增消息接口
     * @param param
     * @return
     */
    public Msg add(AddMsgParam param) {
        //去redis根据userId是否有数据
        //设置一些列参数
        log.info("后台添加消息： param = {}", param);
        Msg entity = Msg.build(param.getUserId(), param.getNickname(), param);
        // 保存商务圈消息
        Msg msg = mongoTemplate.save(entity, "s_msg");
        if(msg == null) {
            return null;
        }
        // 如果musicId不为空维护音乐使用次数
        if(!StringUtils.isEmpty(param.getMusicId())){
            updateUseCount(new ObjectId(param.getMusicId()));
        }
        entity.setSource(1);
        if(null != param.getUserRemindLook()){
            if(null != param.getUserNotLook()){
                List<Integer> collect = param.getUserRemindLook().stream().filter(item -> param.getUserNotLook().contains(item)).collect(Collectors.toList());
                log.info("朋友圈提醒朋友列表:{}, 不给看列表:{}, 交集列表：{}",param.getUserRemindLook(),param.getUserNotLook(), JSONObject.toJSONString(collect));
                param.getUserRemindLook().removeAll(collect);
                if(null == param.getUserRemindLook()){
                    return entity;
                }
            }
        }

        return entity;
    }


    //修改音乐使用次数
    public void updateUseCount(ObjectId id){
        Query query = new Query(Criteria.where("_id").is(id));
        MusicInfo musicInfo = mongoTemplate.findOne(query, MusicInfo.class, "musicInfo");
        if (musicInfo != null) {
            Update update = new Update();
            update.push("useCount", musicInfo.getUseCount() + 1);
            UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MusicInfo.class, "musicInfo");
            log.info("修改音乐使用次数： updateResult = {}", updateResult);
        }
    }

    //修改消息信息
    public ObjectRestResponse update(Msg msg) {
        if (msg == null || msg.getId() == null) {
            return ObjectRestResponse.paramIsEmpty();
        }
        Query query = new Query(Criteria.where("_id").is(msg.getId()));
        Msg oldValue = mongoTemplate.findOne(query, Msg.class, "s_msg");
        if (oldValue == null) {
            return ObjectRestResponse.createFailedResult(ResultCode.IM_MSG_NOT_EXIST_CODE, ResultCode.getMsg(ResultCode.IM_MSG_NOT_EXIST_CODE));
        }
        BeanUtil.copyProperties(msg, oldValue, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
        Msg newValue = mongoTemplate.save(oldValue, "s_msg");
        if(newValue == null) {
            return ObjectRestResponse.createFailedResult(ResultCode.FAILED_CODE, "更新失败");
        }
        return ObjectRestResponse.succ(newValue);
    }

    public ObjectRestResponse deleteById(String id) {
        if (StringUtils.isBlank(id)) {
            return ObjectRestResponse.paramIsEmpty();
        }
        Query query = new Query(Criteria.where("_id").is(id));
        Msg oldValue = mongoTemplate.findOne(query, Msg.class, "s_msg");
        if (oldValue == null) {
            return ObjectRestResponse.createFailedResult(ResultCode.IM_MSG_NOT_EXIST_CODE, ResultCode.getMsg(ResultCode.IM_MSG_NOT_EXIST_CODE));
        }
        DeleteResult deleteResult = mongoTemplate.remove(query, Msg.class, "s_msg");
        if (deleteResult != null && deleteResult.getDeletedCount() == 1) {
            return ObjectRestResponse.succ();
        }
        return ObjectRestResponse.createFailedResult(ResultCode.IM_DELETE_FAIL_CODE, ResultCode.getMsg(ResultCode.IM_DELETE_FAIL_CODE));
    }

}
