package com.wecloud.im.service.impl;

import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.shiro.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.hutool.core.date.DateUtil;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chanjx.utils.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.wecloud.im.chatroom.cache.ChatRoomCacheManager;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.entity.ImMessage;
import com.wecloud.im.friend.entity.ImFriend;
import com.wecloud.im.friend.service.ImFriendService;
import com.wecloud.im.mapper.ImConversationMapper;
import com.wecloud.im.param.ChatRoomMemberPageParam;
import com.wecloud.im.param.DisbandConversationParam;
import com.wecloud.im.param.ExitChatRoomParam;
import com.wecloud.im.param.GroupChatSettingParam;
import com.wecloud.im.param.ImClientSimpleDto;
import com.wecloud.im.param.ImConversationPageParam;
import com.wecloud.im.param.ImConversationQueryParam;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.param.IntoChatRoomParam;
import com.wecloud.im.param.ListConversationMembersParam;
import com.wecloud.im.param.ListConversationParam;
import com.wecloud.im.param.MutedGroupMemberParam;
import com.wecloud.im.param.MutedGroupParam;
import com.wecloud.im.param.SetAdminsParam;
import com.wecloud.im.param.SetHeadPortraitParam;
import com.wecloud.im.param.TransferOwnerParam;
import com.wecloud.im.param.add.ImClientLeaveConversation;
import com.wecloud.im.param.add.ImClientToConversation;
import com.wecloud.im.param.add.ImConversationAttrUpdate;
import com.wecloud.im.param.add.ImConversationCreate;
import com.wecloud.im.param.add.ImConversationDisplayUpdate;
import com.wecloud.im.param.add.ImConversationNameUpdate;
import com.wecloud.im.param.add.ServerImConversationCreate;
import com.wecloud.im.post.Couriers;
import com.wecloud.im.post.MessageBuilder;
import com.wecloud.im.sdk.enums.ChatTypeEnum;
import com.wecloud.im.sdk.enums.FriendStateEnum;
import com.wecloud.im.sdk.enums.GroupChatSettingTypeEnum;
import com.wecloud.im.sdk.enums.GroupRoleEnum;
import com.wecloud.im.sdk.enums.JoinConversationTypeEnum;
import com.wecloud.im.sdk.enums.MutedEnum;
import com.wecloud.im.service.ContextService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.vo.ChatRoomMemberVo;
import com.wecloud.im.vo.ConversationMemberVo;
import com.wecloud.im.vo.ConversationVo;
import com.wecloud.im.vo.ImConversationCreateVo;
import com.wecloud.im.vo.OfflineMsgDto;
import com.wecloud.im.ws.enums.MsgTypeEnum;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.utils.RedisUtils;
import com.wecloud.utils.JsonUtils;
import com.wecloud.utils.SnowflakeUtil;

import static cn.hutool.core.date.DatePattern.CHINESE_DATE_PATTERN;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_ADD_FRIEND;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_ADD_FRIEND_CANCEL;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_SEND_LINK;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_SEND_LINK_CANCEL;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_SEND_PIC;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_FORBID_SEND_PIC_CANCEL;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_MUTED;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_MUTED_CANCEL;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_NAME_CHANGE;
import static com.wecloud.im.ws.enums.MsgTypeEnum.CONVERSATION_SET_GROUP_PORTRAIT;

/**
 * 会话表 服务实现类
 *
 * @author wei
 * @since 2021-05-07
 */
@Slf4j
@Service
@CacheConfig(cacheNames = "convstn")
public class ImConversationServiceImpl extends BaseServiceImpl<ImConversationMapper, ImConversation> implements ImConversationService {

    @Autowired
    private Couriers couriers;

    @Autowired
    private ImConversationMapper imConversationMapper;

    @Autowired
    private ImConversationMembersService imConversationMembersService;

    @Autowired
    private ImConversationService imConversationService;

    @Autowired
    private ImClientService imClientService;

    @Autowired
    private ImMessageService imMessageService;

    @Autowired
    private ContextService contextService;

    @Autowired
    private ImFriendService friendService;

    @Autowired
    private ChatRoomCacheManager chatRoomCacheManager;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveImConversation(ImConversation imConversation) {
        return super.save(imConversation);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ImConversationCreateVo createImConversation(ImConversationCreate imConversationCreate) {
        ImClient createClient = contextService.getImClientIfNotNullOrThrow();
        //  根据appKey查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(createClient.getFkAppid());

        if (BaseEnum.valueOf(ChatTypeEnum.class, imConversationCreate.getChatType()) == null) {
            log.info("会话类型不存在");
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION);
        }

        Integer chatType = imConversationCreate.getChatType();
        String name = imConversationCreate.getName();
        String attributes = imConversationCreate.getAttributes();
        List<String> memberClientIds = imConversationCreate.getClientIds();
        if (ChatTypeEnum.CHAT_ROOM.getCode().equals(chatType)) {
            // 聊天室
            return this.createChatRoom(imConversationCreate, createClient);
        } else if (ChatTypeEnum.SINGLE.getCode().equals(chatType)) {
            ImConversation conversation = this.createPrivateChat(createClient, memberClientIds.get(0), name,
                    attributes);
            ImConversationCreateVo imConversationCreateVo = new ImConversationCreateVo();
            imConversationCreateVo.setId(conversation.getId());
            return imConversationCreateVo;
        } else if (ChatTypeEnum.NORMAL_GROUP.getCode().equals(chatType)) {
            ImConversation conversation = this.createGroupChat(imApplication, createClient, memberClientIds, name, attributes);
            ImConversationCreateVo imConversationCreateVo = new ImConversationCreateVo();
            imConversationCreateVo.setId(conversation.getId());
            return imConversationCreateVo;
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ImConversation serverCreateImConversation(ServerImConversationCreate imConversationCreate) {
        if (ChatTypeEnum.CHAT_ROOM.equals(imConversationCreate.getChatType())) {
            // 聊天室
            throw new BusinessException("不允许创建聊天室");
        }

        if (CollectionUtils.isEmpty(imConversationCreate.getMembers())) {
            log.info("未找到群成员信息");
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION);
        }

        ImApplication imApplication = imConversationCreate.getApplication();
        ImClient creator = imConversationCreate.getCreator();
        Long appId = creator.getFkAppid();

        ChatTypeEnum chatType = imConversationCreate.getChatType();
        String name = imConversationCreate.getName();
        String attributes = imConversationCreate.getAttributes();
        List<String> memberClientIds = imConversationCreate.getMembers().stream().map(ImClient::getClientId).collect(Collectors.toList());
        if (ChatTypeEnum.SINGLE.equals(chatType)) {
            return this.createPrivateChat(creator, memberClientIds.get(0), name, attributes);
        } else if (ChatTypeEnum.NORMAL_GROUP.equals(chatType)) {
            return this.createGroupChat(imApplication, creator, memberClientIds, name, attributes);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addClientToConversation(ImClientToConversation imClientToConversation) {

        // 获取当前client
        ImClient createClient = contextService.getImClientIfNotNullOrThrow();
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(createClient.getFkAppid());

        ImConversation imConversation = getById(imClientToConversation.getConversationId());

        ImClient imClientSender = imClientService.getCurrentClient();

        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, imApplication.getId())

                .eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).ne(ImConversationMembers::getFkClientId, imClientSender.getId()));
        if (membersList.isEmpty()) {
            log.info("membersList为空,toConversationId:" + imClientToConversation.getConversationId());
            throw new BusinessException("没有成员");
        }

        int needAddCount = 0;

        // 将他人添加到会话
        for (String id : imClientToConversation.getClientIds()) {
            ImClient clientToConversation = imClientService.getOne(new QueryWrapper<ImClient>().lambda().eq(ImClient::getFkAppid, imApplication.getId()).eq(ImClient::getClientId, id));
            if (clientToConversation == null) {
                throw new BusinessException(ApiCode.CLIENT_NOT_FOUNT);
            }
            //  判断用户是否已经在该会话
            ImConversationMembers members = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, imApplication.getId()).eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).eq(ImConversationMembers::getFkClientId, clientToConversation.getId()));

            // 已经在该会话 则跳过
            if (members != null) {
                continue;
            }
            Date now = new Date();
            Long imConversationMembersId2 = SnowflakeUtil.getId();
            ImConversationMembers imConversationMembers2 = new ImConversationMembers();
            imConversationMembers2.setUpdateTime(new Date());
            imConversationMembers2.setId(imConversationMembersId2);
            imConversationMembers2.setCreateTime(new Date());
            imConversationMembers2.setFkAppid(imApplication.getId());
            imConversationMembers2.setFkConversationId(imClientToConversation.getConversationId());
            imConversationMembers2.setFkClientId(clientToConversation.getId());
            imConversationMembers2.setClientId(clientToConversation.getClientId());
            imConversationMembers2.setMuted(MutedEnum.NO.getCode());
            imConversationMembers2.setRole(GroupRoleEnum.NORMAL.getCode());
            // 加入会话方式
            // 加入会话方式固定为 邀请
            imConversationMembers2.setJoinTypeCode(JoinConversationTypeEnum.BE_INVITED.getCode());
            String memberJoinTypeMsg = "";
            if (ChatTypeEnum.SINGLE.getCode().equals(imConversation.getChatType())) {
                memberJoinTypeMsg = "由 <@>" + createClient.getClientId() + "<@>邀请加入该会话";
            } else if (ChatTypeEnum.NORMAL_GROUP.getCode().equals(imConversation.getChatType()) || ChatTypeEnum.THOUSAND_GROUP.getCode().equals(imConversation.getChatType())) {
                memberJoinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + createClient.getClientId() + "<@>邀请进聊";
            } else if (ChatTypeEnum.CHAT_ROOM.getCode().equals(imConversation.getChatType())) {
                memberJoinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + createClient.getClientId() + "<@>邀请进入聊天室";
            }

            imConversationMembersService.save(imConversationMembers2);

            needAddCount++;

            // ws邀请事件通知给群内其他人 ----------
            // 生成消息id
            Map<String, Object> content = new HashMap<>();
            content.put("operator", imClientSender.getClientId()); //操作的client ID
            content.put("passivityOperator", clientToConversation.getClientId()); //被操作的client ID
            ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.INVITE_CLIENT_JOIN_CONVERSATION, imApplication, createClient, imConversation, JsonUtils.encodeJson(content));
            boolean save = imMessageService.save(imMessage);
            // 发送给在群内的成员
            sendMsgToMembers(imConversation, membersList, createClient, imMessage, content);
            // 发送给被邀请人
            couriers.deliver(imMessage, content, createClient, clientToConversation, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
        }

        // 将群成员数量减
        imConversationMapper.addMemberCount(imApplication.getId(), imClientToConversation.getConversationId(), needAddCount);
        ImConversationQueryVo conversation = imConversationMapper.getImConversationById(imClientToConversation.getConversationId());
        if (conversation.getMemberCount() > 2000) {
            // 升级为万人群
            imConversationMapper.upgradeToThousandChat(imApplication.getId(), imClientToConversation.getConversationId());
        }

        return true;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delClientToConversation(ImClientToConversation imClientToConversation) throws Exception {
        // 获取当前client
        ImClient createClient = contextService.getImClientIfNotNullOrThrow();
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(createClient.getFkAppid());
        ImClient imClientSender = imClientService.getCurrentClient();
        // 查询当前会话
        ImConversation imConversation = this.getById(imClientToConversation.getConversationId());
        // 判断是否为群主
        ImConversationMembers conversationMember = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).eq(ImConversationMembers::getFkClientId, imClientSender.getId()));
        if (conversationMember == null || !GroupRoleEnum.OWNER.getCode().equals(conversationMember.getRole())) {
            return false;
        }
        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, imApplication.getId()).eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).ne(ImConversationMembers::getFkClientId, imClientSender.getId()));
        if (membersList.isEmpty()) {
            log.info("membersList为空,toConversationId:" + imClientToConversation.getConversationId());
            throw new BusinessException("没有成员");
        }

        int needAddCount = 0;
        // 将client从会话移除
        for (String clientId : imClientToConversation.getClientIds()) {
            ImClient clientToBeRemove = imClientService.getOne(new QueryWrapper<ImClient>().lambda().eq(ImClient::getFkAppid, imApplication.getId()).eq(ImClient::getClientId, clientId));

            //   判断用户是否已经在该会话
            ImConversationMembers members = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, imApplication.getId()).eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).eq(ImConversationMembers::getFkClientId, clientToBeRemove.getId()));
            if (members == null) {
                continue;
            }
            imConversationMembersService.deleteImConversationMembers(members.getId());

            // ws移除事件通知给群内其他人 ----------
            needAddCount--;
            Map<String, Object> content = new HashMap<>();
            content.put("operator", imClientSender.getClientId()); //操作的client ID
            content.put("passivityOperator", clientToBeRemove.getClientId()); //被操作的client ID

            ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.REMOVE_CLIENT_CONVERSATION, imApplication, createClient, imConversation, JsonUtils.encodeJson(content));
            imMessageService.save(imMessage);

            membersList.removeIf(e -> e.getId().equals(members.getId()));

            Long conversationId = imClientToConversation.getConversationId();
            Long appId = imApplication.getId();
            String sender = imClientSender.getClientId();
            Integer msgType = imMessage.getMsgType();
            sendMsgToMembers(imConversation, membersList, createClient, imMessage, content);
        }

        // 将群成员数量减
        imConversationMapper.addMemberCount(imApplication.getId(), imClientToConversation.getConversationId(), needAddCount);
        return true;
    }

    @Override
    public void disband(DisbandConversationParam param) {
        ImConversation imConversation = imConversationMapper.selectById(param.getConversationId());
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        if (imConversation == null) {
            throw new BusinessException("查无会话信息");
        }
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        if (ChatTypeEnum.CHAT_ROOM.getCode().equals(imConversation.getChatType())) {
            // 聊天室事件发送逻辑
            disbandChatRoom(param.getConversationId(), currentClient);
            return;
        }
        // 判断是否为群主
        ImConversationMembers conversationMember = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, param.getConversationId()).eq(ImConversationMembers::getFkClientId, currentClient.getId()));
        if (conversationMember == null || !GroupRoleEnum.OWNER.getCode().equals(conversationMember.getRole())) {
            throw new BusinessException("非群主无权限解散群聊");
        }
        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, currentClient.getFkAppid()).eq(ImConversationMembers::getFkConversationId, param.getConversationId()).ne(ImConversationMembers::getFkClientId, currentClient.getId()));
        if (membersList.isEmpty()) {
            throw new BusinessException("群聊成员列表为空");
        }
        // 删除所有成员
        imConversationMembersService.deleteByConversationId(param.getConversationId());
        // 保存事件消息

        Map<String, Object> content = new HashMap<>();
        content.put("operator", currentClient.getClientId());
        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.CONVERSATION_DISBAND, imApplication, currentClient, imConversation, JsonUtils.encodeJson(content));
        imMessageService.save(imMessage);

        sendMsgToMembers(imConversation, membersList, currentClient, imMessage, content);
    }

    /**
     * 解散聊天室
     * @param conversationId
     * @param currentClient
     */
    private void disbandChatRoom(Long conversationId, ImClient currentClient) {
        ImConversation imConversation = imConversationMapper.selectById(conversationId);
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        Map<String, String> chatRoomMembers = chatRoomCacheManager.findOnlineClientsByChatRoomId(conversationId);
        if (chatRoomMembers.isEmpty()) {
            throw new BusinessException("聊天室成员列为空");
        }
        // 保存事件消息

        Map<String, Object> content = new HashMap<>();
        content.put("operator", currentClient.getClientId());

        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.CONVERSATION_DISBAND, imApplication, currentClient, imConversation, JsonUtils.encodeJson(content));
        // 遍历发送给已在群内的成员
        List<Long> fkClientIds = chatRoomMembers.keySet().stream().map(key -> Long.valueOf(key.split(RedisUtils.SPLIT)[0])).collect(Collectors.toList());
        List<ImClient> clientReceivers = imClientService.list(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getFkAppid, imApplication.getId()).in(ImClient::getId, fkClientIds));

        for (ImClient receiver : clientReceivers) {
            // 投递消息
            try {
                couriers.deliver(imMessage, content, currentClient, receiver, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
            } catch (Exception e) {
                log.info("下发群会话事件失败，事件类型 {} 接收人 {}", imMessage.getMsgType(), JSON.toJSONString(receiver));
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean leaveConversation(ImClientLeaveConversation imClientToConversation) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        ImConversation imConversation = imConversationService.getById(imClientToConversation.getConversationId());
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        if (imConversation == null) {
            throw new BusinessException("查无会话消息");
        }

        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).orderByAsc(ImConversationMembers::getCreateTime).ne(ImConversationMembers::getFkClientId, currentClient.getId()));

        if (membersList.isEmpty()) {
            log.info("membersList为空,toConversationId:" + imClientToConversation.getConversationId());
            throw new BusinessException("会话中查无群人员");
        }

        //   判断用户是否在该会话
        ImConversationMembers members = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, currentClient.getFkAppid()).eq(ImConversationMembers::getFkConversationId, imClientToConversation.getConversationId()).eq(ImConversationMembers::getFkClientId, currentClient.getId()));

        // 将client从会话移除
        boolean b = imConversationMembersService.deleteImConversationMembers(members.getId());
        if (!b) {
            throw new BusinessException("退出群聊错误");
        }

        // 将群成员数量减1
        imConversationMapper.addMemberCount(currentClient.getFkAppid(), imClientToConversation.getConversationId(), -1);

        // ws 退出事件通知给群内其他人 ----------


        // 生成消息id
        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.LEAVE_CONVERSATION, imApplication, currentClient, imConversation, "");
        //  保存消息至消息表
        boolean save = imMessageService.save(imMessage);
        if (!save) {
            throw new BusinessException("退出群聊错误");
        }

        sendMsgToMembers(imConversation, membersList, currentClient, imMessage, null);
        // 群主退出 转移给下一个人
        if (GroupRoleEnum.OWNER.getCode().equals(members.getRole())) {
            ImConversationMembers conversationMember = membersList.get(0);
            conversationMember.setRole(GroupRoleEnum.OWNER.getCode());
            // 群主转移给下一个人
            imConversationMembersService.updateById(conversationMember);
        }
        return true;
    }


    /**
     * 群主转让
     *
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transferOwner(TransferOwnerParam param) {
        // 获取当前client
        ImClient createClient = contextService.getImClientIfNotNullOrThrow();
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(createClient.getFkAppid());
        ImClient imClientSender = imClientService.getCurrentClient();

        // 判断操作人是否为群主
        ImConversationMembers currentMember = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, param.getConversationId()).eq(ImConversationMembers::getFkClientId, imClientSender.getId()));
        if (currentMember == null || !GroupRoleEnum.OWNER.getCode().equals(currentMember.getRole())) {
            throw new BusinessException("非群主无权操作");
        }
        // 将自己设置为普通群成员
        currentMember.setRole(GroupRoleEnum.NORMAL.getCode());
        imConversationMembersService.updateById(currentMember);
        // 将入参成员设置为群主
        ImClient transferClient = imClientService.getCacheImClient(imApplication.getId(), param.getClientId());
        ImConversationMembers transferMember = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, param.getConversationId()).eq(ImConversationMembers::getFkClientId, transferClient.getId()));
        transferMember.setRole(GroupRoleEnum.OWNER.getCode());
        imConversationMembersService.updateById(transferMember);
        return true;
    }

    /**
     * 设置管理员
     *
     * @param param
     * @return
     */
    @Override
    public Boolean setAdmins(SetAdminsParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        // 判断操作人是否为群主
        ImConversationMembers currentMember = imConversationMembersService.getOne(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, param.getConversationId()).eq(ImConversationMembers::getFkClientId, currentClient.getId()));
        if (currentMember == null || !GroupRoleEnum.OWNER.getCode().equals(currentMember.getRole())) {
            throw new BusinessException("非群主无权操作");
        }
        List<ImClientSimpleDto> simpleClients = imClientService.getSimpleClients(currentClient.getFkAppid(), param.getClientIds());
        if (CollectionUtils.isEmpty(simpleClients)) {
            throw new BusinessException("未查询得到真实管理员信息");
        }
        List<Long> fkClientIds = simpleClients.stream().filter(s -> !currentMember.getId().equals(s.getId())).map(ImClientSimpleDto::getId).collect(Collectors.toList());
        imConversationMembersService.setAdminsForConversation(param, fkClientIds);
        return true;
    }

    /**
     * 群禁言
     *
     * @param param
     * @return
     */
    @Override
    public Boolean mutedGroup(MutedGroupParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        if (!imConversationService.isBelongToRole(currentClient.getClientId(), param.getConversationId(), Lists.newArrayList(GroupRoleEnum.OWNER.getCode(), GroupRoleEnum.ADMIN.getCode()))) {
            // 当前操作人不属于群主或管理人员
            throw new BusinessException("操作人既不是群主也不是群管理员，无权限操作");
        }
        ImConversation muteGroupParam = new ImConversation();
        muteGroupParam.setId(param.getConversationId());
        muteGroupParam.setUpdateTime(new Date());
        muteGroupParam.setMuted(param.getMutedType());
        imConversationMapper.updateById(muteGroupParam);
        deleteCacheImConversationById(param.getConversationId());
        // 下发事件通知 开启、取消 群禁言
        Integer msgType = MutedEnum.NO.getCode().equals(param.getMutedType()) ? CONVERSATION_MUTED_CANCEL.getUriCode() : MsgTypeEnum.CONVERSATION_MUTED.getUriCode();
        return true;
    }

    @Override
    public Boolean mutedGroupMember(MutedGroupMemberParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        if (!imConversationService.isBelongToRole(currentClient.getClientId(), param.getConversationId(), Lists.newArrayList(GroupRoleEnum.OWNER.getCode(), GroupRoleEnum.ADMIN.getCode()))) {
            // 当前操作人不属于群主或管理人员
            throw new BusinessException("操作人既不是群主也不是群管理员，无权限操作");
        }
        ListConversationMembersParam getMutedMemberParam = new ListConversationMembersParam();
        getMutedMemberParam.setConversationId(param.getConversationId());
        getMutedMemberParam.setClientIds(param.getClientIds());
        List<ConversationMemberVo> mutedMembers = imConversationMembersService.getImConversationMembersList(getMutedMemberParam);
        List<ImConversationMembers> mutedMemberList = Lists.newArrayList();
        for (ConversationMemberVo mutedMember : mutedMembers) {
            ImConversationMembers saveMutedMember = new ImConversationMembers();
            saveMutedMember.setId(mutedMember.getId());
            saveMutedMember.setUpdateTime(new Date());
            saveMutedMember.setMuted(param.getMutedType());
            mutedMemberList.add(saveMutedMember);
        }
        imConversationMembersService.updateBatchById(mutedMemberList);
        return true;
    }

    /**
     * 判断当前操作人是否为指定角色成员
     *
     * @param currentClientId
     * @param conversationId
     * @return
     */
    @Override
    public Boolean isBelongToRole(String currentClientId, Long conversationId, List<Integer> roles) {
        // 获取 群主和群管理员列表
        ListConversationMembersParam getMemberParam = new ListConversationMembersParam();
        getMemberParam.setConversationId(conversationId);
        getMemberParam.setRoles(roles);
        List<ConversationMemberVo> members = imConversationMembersService.getImConversationMembersList(getMemberParam);
        if (CollectionUtils.isEmpty(members)) {
            return false;
        }
        for (ConversationMemberVo member : members) {
            if (currentClientId.equals(member.getClientId())) {
                return true;
            }
        }
        return false;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Boolean> saveOrUpdateName(ImConversationNameUpdate param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        ImConversation imConversation = imConversationService.getById(param.getConversationId());
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        if (!this.isBelongToRole(currentClient.getClientId(), param.getConversationId(), Lists.newArrayList(GroupRoleEnum.OWNER.getCode(), GroupRoleEnum.ADMIN.getCode()))) {
            throw new BusinessException("操作人既不是群主也不是群管理员，无权限操作");
        }
        // 查询该会话所有成员
        List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkConversationId, param.getConversationId()).ne(ImConversationMembers::getFkClientId, currentClient.getId()));
        imConversation.setName(param.getName());
        boolean b = imConversationService.updateById(imConversation);
        // 删除redis中该会话的缓存
        deleteCacheImConversationById(param.getConversationId());
        if (b) {
            // 内容
            HashMap<String, String> content = Maps.newHashMap();
            content.put("name", imConversation.getName());
            // 发送消息至成员
            ImMessage message = MessageBuilder.buildEventMessage(MsgTypeEnum.CONVERSATION_NAME_CHANGE, imApplication, currentClient, imConversation, "");
            sendMsgToMembers(imConversation, membersList, currentClient, message, content);
            return ApiResult.ok();
        } else {
            return ApiResult.fail();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateAttr(ImConversationAttrUpdate imConversationAttrUpdate) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        ImConversation imConversation = imConversationService.getById(imConversationAttrUpdate.getConversationId());
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        imConversation.setAttributes(imConversationAttrUpdate.getAttributes());
        boolean b = imConversationService.updateById(imConversation);
        // 删除redis中该会话的缓存
        deleteCacheImConversationById(imConversationAttrUpdate.getConversationId());
        if (b) {
            if (ChatTypeEnum.CHAT_ROOM.getCode().equals(imConversation.getChatType())) {
                // 聊天室事件发送逻辑
                chatRoomAttrChanged(imConversation, currentClient);
                return;
            }
            // 查询该会话所有成员
            List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, currentClient.getFkAppid()).eq(ImConversationMembers::getFkConversationId, imConversationAttrUpdate.getConversationId()).ne(ImConversationMembers::getFkClientId, currentClient.getId()));
            // ws下发拓展字段变动事件
            HashMap<String, String> content = Maps.newHashMap();
            content.put("attributes", imConversation.getAttributes());
            // 向群成员发送拓展字段变动事件
            Long conversationId = imConversationAttrUpdate.getConversationId();
            Long appId = currentClient.getFkAppid();
            String sender = currentClient.getClientId();
            ImMessage message = new ImMessage();
            message.setWithdraw(Boolean.FALSE);
            message.setEvent(Boolean.TRUE);
            message.setMsgType(MsgTypeEnum.CONVERSATION_EXPAND_FIELD_CHANGE.getUriCode());
            sendMsgToMembers(imConversation, membersList, currentClient, message, null);
        } else {
            throw new BusinessException("修改错误");
        }
    }


    /**
     * 解散聊天室
     * @param imConversation
     * @param currentClient
     */
    private void chatRoomAttrChanged(ImConversation imConversation, ImClient currentClient) {
        Map<String, String> chatRoomMembers = chatRoomCacheManager.findOnlineClientsByChatRoomId(imConversation.getId());
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imConversation.getFkAppid());
        if (chatRoomMembers.isEmpty()) {
            throw new BusinessException("聊天室成员列为空");
        }
        // 保存事件消息
        HashMap<String, Object> contentMap = Maps.newHashMap();
        contentMap.put("attributes", imConversation.getAttributes());

        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.CONVERSATION_EXPAND_FIELD_CHANGE, imApplication, currentClient, imConversation, JsonUtils.encodeJson(contentMap));

        // 遍历发送给已在群内的成员
        List<Long> fkClientIds = chatRoomMembers.keySet().stream().map(key -> Long.valueOf(key.split(RedisUtils.SPLIT)[0])).collect(Collectors.toList());
        List<ImClient> clientReceivers = imClientService.list(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getFkAppid, imApplication.getId()).in(ImClient::getId, fkClientIds));

        for (ImClient receiver : clientReceivers) {
            // 投递消息
            try {
                couriers.deliver(imMessage, contentMap, currentClient, receiver, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
            } catch (Exception e) {
                log.info("下发群会话事件失败，事件类型 {} 接收人 {}", imMessage.getMsgType(), JSON.toJSONString(receiver));
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateImConversation(ImConversation imConversation) {
        return super.updateById(imConversation);
    }


    /**
     * 是否单向隐藏会话","云端聊天记录不删除;假设有A和B两个用户,A删会话,B还能发; 如果B发了消息,A这边要重新把会话显示出来,并能显示之前的聊天记录"
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateDisplayConversation(ImConversationDisplayUpdate imConversationDisplayUpdate) {
        // 获取当前client
        ImClient curentClient = contextService.getImClientIfNotNullOrThrow();
        for (Long id : imConversationDisplayUpdate.getConversationIds()) {
            // 修改为删除隐藏状态
            boolean update = imConversationMembersService.update(new UpdateWrapper<ImConversationMembers>().set("display_status", imConversationDisplayUpdate.getDisplayStatus()).eq("fk_client_id", curentClient.getId()).eq("fk_conversation_id", id).eq("fk_appid", curentClient.getFkAppid()));
        }
        return true;
    }

    @Override
//    @Cacheable(key = "#p0")
    public ImConversationQueryVo getCacheImConversationById(Long id) {
        return imConversationMapper.getImConversationById(id);
    }

    @Override
    @CacheEvict(key = "#p0")
    public void deleteCacheImConversationById(Long id) {
    }

    @Override
    public Paging<ImConversationQueryVo> getImConversationPageList(ImConversationPageParam imConversationPageParam) {
        Page<ImConversationQueryVo> page = new PageInfo<>(imConversationPageParam, OrderItem.desc(getLambdaColumn(ImConversation::getCreateTime)));
        IPage<ImConversationQueryVo> iPage = imConversationMapper.getImConversationPageList(page, imConversationPageParam);
        return new Paging<ImConversationQueryVo>(iPage);
    }

    @Override
    public List<ConversationVo> getMyImConversationListAndMsgCount() {
        try {
            // 获取当前client
            ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
            // 查询用户加入的所有会话 与每个会话的未读条数 成员
            List<ConversationVo> myImConversationListAndMsgCount = imConversationMapper.getMyImConversationListAndMsgCount(currentClient.getId(), currentClient.getClientId(), null);
            if (myImConversationListAndMsgCount.isEmpty()) {
                return Collections.emptyList();
            }

            // 根据top进行排序
            List<ConversationVo> topList = myImConversationListAndMsgCount.stream().filter(ConversationVo::getTop).collect(Collectors.toList());
            List<ConversationVo> normalList =
                    myImConversationListAndMsgCount.stream().filter(vo -> !vo.getTop()).collect(Collectors.toList());

            myImConversationListAndMsgCount = new ArrayList<>(topList);
            myImConversationListAndMsgCount.addAll(normalList);

            // 返回的
            List<ConversationVo> myImConversationListAndMsgCountNew = new ArrayList<>();

            // 转换json格式
            for (ConversationVo myconversationlistvo : myImConversationListAndMsgCount) {
                HashMap attributess = JsonUtils.json2Map(myconversationlistvo.getAttribute());
                myconversationlistvo.setAttributes(attributess);

                // 查询会话的最后一条消息
                OfflineMsgDto lastMsg = imMessageService.getLastMsgByConversationId(myconversationlistvo.getId(), currentClient.getId());
                myconversationlistvo.setLastMsg(lastMsg);
                if (myconversationlistvo.getBeAtCount() > 0) {
                    myconversationlistvo.setIsBeAt(Boolean.TRUE);
                }
                myImConversationListAndMsgCountNew.add(myconversationlistvo);

            }
            log.info("{} 查询加入的会话列表结果 {}", currentClient.getClientId(), JSON.toJSONString(myImConversationListAndMsgCountNew));
            return myImConversationListAndMsgCountNew;
        } catch (Exception e) {
            log.info("查询所有会话异常 ", e);
            throw new BusinessException("查询错误，稍后重试");
        }

    }

    @Override
    public ConversationVo infoImConversationAndMsgCount(ImConversationQueryParam param) {
        try {
            // 获取当前client
            ImClient client = contextService.getImClientIfNotNullOrThrow();
            if (param.getChatType() != null && param.getChatType() == 4) {
                // 聊天室
                ImConversation imConversation = imConversationMapper.selectById(param.getId());
                if (imConversation != null) {
                    ConversationVo conversationVo = new ConversationVo();
                    BeanUtils.copyProperties(imConversation, conversationVo);
                    return conversationVo;
                }
            }
            // 查询用户加入的所有会话 与每个会话的未读条数 成员
            List<ConversationVo> conversationList = imConversationMapper.getMyImConversationListAndMsgCount(client.getId(), client.getClientId(), param.getId());
            if (CollectionUtils.isEmpty(conversationList)) {
                return null;
            }
            ConversationVo conversationVo = conversationList.get(0);
            HashMap attributess = JsonUtils.json2Map(conversationVo.getAttribute());
            conversationVo.setAttributes(attributess);
            // 查询会话的最后一条消息
            OfflineMsgDto lastMsg = imMessageService.getLastMsgByConversationId(conversationVo.getId(), client.getId());
            conversationVo.setLastMsg(lastMsg);
            return conversationVo;
        } catch (Exception e) {
            log.info("添加或修改会话名称异常 ", e);
            throw new BusinessException("修改会话名称错误");
        }
    }


    @Override
    public List<ImConversation> getMyImConversationList() {
        ImClient client = imClientService.getCurrentClient();
        return imConversationMapper.getMyImConversationList(client.getId());
    }

    @Override
    public Long getRepetitionConversationAttributes(Long clientId1, Long clientId2, String attributes) {
        return imConversationMapper.getRepetitionConversationAttributes(clientId1, clientId2, attributes);
    }

    @Override
    public Boolean groupChatSetting(GroupChatSettingParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(currentClient.getFkAppid());
        if (!imConversationService.isBelongToRole(currentClient.getClientId(), param.getGroupId(), Lists.newArrayList(GroupRoleEnum.OWNER.getCode(), GroupRoleEnum.ADMIN.getCode()))) {
            // 当前操作人不属于群主或管理人员
            throw new BusinessException("操作人既不是群主也不是群管理员，无权限操作");
        }

        ImConversation group = getOne(Wrappers.<ImConversation>lambdaQuery().eq(ImConversation::getFkAppid, currentClient.getFkAppid()).eq(ImConversation::getId, param.getGroupId()));

        if (group == null) {
            throw new BusinessException("群不存在");
        }

        this.groupChatSetting(imApplication, currentClient, group, param);
        afterGroupSetting(imApplication, currentClient, group, param);
        return true;
    }

    @Override
    public Boolean groupChatSettingForSdk(GroupChatSettingParam param) {
        ImClient operatorClient = imClientService.getCacheImClient(SecurityUtils.getCurrentAppId(), param.getOperatorClientId());
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(operatorClient.getFkAppid());
        ImConversation group = getOne(Wrappers.<ImConversation>lambdaQuery().eq(ImConversation::getFkAppid, SecurityUtils.getCurrentAppId()).eq(ImConversation::getId, param.getGroupId()));

        if (group == null) {
            throw new BusinessException("群不存在");
        }

        this.groupChatSetting(imApplication, operatorClient, group, param);
        afterGroupSetting(imApplication, operatorClient, group, param);
        return true;
    }

    /**
     * 群消息设置后的事件
     * @Author luozh
     * @Date 2022年05月17日 12:01:48
     * @param application
     * @param operatorClient
     * @param group
     * @param param
     * @Return
     */
    private void afterGroupSetting(ImApplication application, ImClient operatorClient, ImConversation group, GroupChatSettingParam param) {
        // 删除redis中该会话的缓存
        deleteCacheImConversationById(group.getId());
        // 下发事件
        GroupChatSettingTypeEnum type = param.getType();
        // 消息内容
        Map<String, Object> content = new HashMap<>();
        content.put("operator", operatorClient.getClientId());
        switch (type) {
            case NAME:
                content.put("name", param.getName());
                break;
            case HEAD_PORTRAIT:
                content.put("headPortrait", param.getHeadPortrait());
                break;
            default:
                break;
        }
        // 查询该会话所有成员
        List<ImConversationMembers> membersList =
                imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, application.getId())
                        .eq(ImConversationMembers::getFkConversationId, group.getId()));
        // 发送群聊配置变化消息
        MsgTypeEnum msgType = getMsgType(type, param.getOn());
        if (!membersList.isEmpty()) {
            // 保存事件消息
            ImMessage imMessage = MessageBuilder.buildEventMessage(msgType, application, operatorClient, group, JsonUtils.encodeJson(content));
            imMessageService.save(imMessage);
            // 发送消息至群成员
            sendMsgToMembers(group, membersList, operatorClient, imMessage, content);
        }
    }

    private Boolean groupChatSetting(ImApplication application, ImClient operatorClient, ImConversation conversation, GroupChatSettingParam param) {
        // 根据类型处理
        GroupChatSettingTypeEnum typeEnum = param.getType();
        Boolean on = param.getOn();
        switch (typeEnum) {
            case FORBID_ADD_FRIEND:
                conversation.setForbidAddFriend(on);
                break;
            case FORBID_SEND_PIC:
                conversation.setForbidSendPic(on);
                break;
            case FORBID_SEND_LINK:
                conversation.setForbidSendLink(on);
                break;
            case HEAD_PORTRAIT:
                conversation.setHeadPortrait(param.getHeadPortrait());
                break;
            case NAME:
                conversation.setName(param.getName());
                break;
            case MUTED:
                int muted = 1;
                if (on) {
                    muted = 2;
                }
                conversation.setMuted(muted);
                break;
            default:
                throw new BusinessException("设置类型错误");
        }
        // 更新会话
        boolean success = updateById(conversation);
        return success;
    }

    @Override
    public Boolean setHeadPortrait(SetHeadPortraitParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(currentClient.getFkAppid());
        if (!imConversationService.isBelongToRole(currentClient.getClientId(), param.getConversationId(), Lists.newArrayList(GroupRoleEnum.OWNER.getCode(), GroupRoleEnum.ADMIN.getCode()))) {
            // 当前操作人不属于群主或管理人员
            throw new BusinessException("操作人既不是群主也不是群管理员，无权限操作");
        }
        ImConversation imConversation = imConversationService.getById(param.getConversationId());
        imConversation.setHeadPortrait(param.getHeadPortrait());
        boolean success = imConversationService.updateById(imConversation);
        if (success) {
            // 删除redis中该会话的缓存
            deleteCacheImConversationById(param.getConversationId());
            // 查询该会话所有成员
            List<ImConversationMembers> membersList = imConversationMembersService.list(new QueryWrapper<ImConversationMembers>().lambda().eq(ImConversationMembers::getFkAppid, currentClient.getFkAppid()).eq(ImConversationMembers::getFkConversationId, param.getConversationId()).ne(ImConversationMembers::getFkClientId, currentClient.getId()));
            ImMessage message = MessageBuilder.buildEventMessage(MsgTypeEnum.CONVERSATION_SET_GROUP_PORTRAIT, imApplication, currentClient, imConversation, "");
            sendMsgToMembers(imConversation, membersList, currentClient, message, null);
        } else {
            throw new BusinessException("修改错误");
        }
        return true;
    }

    @Override
    public Boolean intoChatRoom(IntoChatRoomParam param) {
        if (param.getPlatform() == null) {
            throw new BusinessException("平台入参不可为空");
        }
        // 获取当前client
        ImClient imClientSender = contextService.getImClientIfNotNullOrThrow();
        //  根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(imClientSender.getFkAppid());
        // 获取会话
        ImConversation imConversation = getById(param.getChatRoomId());

        // 添加到会话成员列表中
        ImClient clientToConversation = imClientService.getOne(new QueryWrapper<ImClient>().lambda().eq(ImClient::getFkAppid, imApplication.getId()).eq(ImClient::getClientId, param.getClientId()));
        if (clientToConversation == null) {
            throw new BusinessException(ApiCode.CLIENT_NOT_FOUNT);
        }
        // 获取房间所有成员 key是 client的主键id:platform, val是 ip
        Map<String, String> chatRoomMembers = chatRoomCacheManager.findOnlineClientsByChatRoomId(param.getChatRoomId());
        for (String key : chatRoomMembers.keySet()) {
            if (clientToConversation.getId().toString().equals(key.split(RedisUtils.SPLIT)[0])) {
                return Boolean.FALSE;
            }
        }
        // 修改群成员数量
        imConversationMapper.addMemberCount(imApplication.getId(), param.getChatRoomId(), 1);
        chatRoomCacheManager.intoRoom(clientToConversation.getId(), param.getChatRoomId(), param.getPlatform());

        // ws邀请事件通知给群内其他人 ----------
        Map<String, Object> content = new HashMap<>();
        content.put("operator", imClientSender.getClientId()); //操作的client ID
        content.put("passivityOperator", clientToConversation.getClientId()); //被操作的client ID

        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.INVITE_CLIENT_JOIN_CONVERSATION, imApplication, imClientSender, imConversation, "");

        imMessage.setContent(JsonUtils.encodeJson(content));
        imMessage.setId(SnowflakeUtil.getId());
        imMessage.setMsgType(MsgTypeEnum.INVITE_CLIENT_JOIN_CONVERSATION.getUriCode());
        imMessage.setCreateTime(new Date());
        imMessage.setFkAppid(imApplication.getId());
        imMessage.setSender(imClientSender.getId());
        imMessage.setWithdraw(false);
        imMessage.setEvent(true);
        imMessage.setSystemFlag(false);
        imMessage.setSendStatus(2);
        imMessage.setFkConversationId(param.getChatRoomId());

        // 遍历发送给已在群内的成员
        List<Long> fkClientIds = chatRoomMembers.keySet().stream().map(key -> Long.valueOf(key.split(RedisUtils.SPLIT)[0])).collect(Collectors.toList());
        List<ImClient> clientReceivers = imClientService.list(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getFkAppid, imApplication.getId()).in(ImClient::getId, fkClientIds));

        for (ImClient receiver : clientReceivers) {
            // 投递消息
            try {
                couriers.deliver(imMessage, content, imClientSender, receiver, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
            } catch (Exception e) {
                log.info("下发群会话事件失败，事件类型 {} 接收人 {}", imMessage.getMsgType(), JSON.toJSONString(receiver));
            }
        }

        return true;
    }

    @Override
    public Boolean exitChatRoom(ExitChatRoomParam param) {
        // 获取当前client
        ImClient currentClient = contextService.getImClientIfNotNullOrThrow();
        // 根据appId查询application
        ImApplication imApplication = contextService.getImApplicationIfNotNullOrThrow(currentClient.getFkAppid());
        ImConversation imConversation = imConversationService.getById(param.getChatRoomId());
        if (imConversation == null) {
            throw new BusinessException("查无会话消息");
        }
        // 获取房间所有成员 key是 client的主键id:platform, val是 ip
        Map<String, String> chatRoomMembers = chatRoomCacheManager.findOnlineClientsByChatRoomId(param.getChatRoomId());
        if (chatRoomMembers.isEmpty()) {
            throw new BusinessException("会话中查无群人员");
        }
        Boolean inRoom = Boolean.FALSE;
        for (String key : chatRoomMembers.keySet()) {
            if (currentClient.getId().toString().equals(key.split(RedisUtils.SPLIT)[0])) {
                inRoom = Boolean.TRUE;
                continue;
            }
        }
        if (!inRoom) {
            // 不在房间
            return Boolean.FALSE;
        }
        // 将群成员数量减1
        imConversationMapper.addMemberCount(currentClient.getFkAppid(), param.getChatRoomId(), -1);
        chatRoomCacheManager.exitRoom(currentClient.getId(), param.getChatRoomId(), param.getPlatform());

        // ws 退出事件通知给房间内其他人 ----------
        ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.LEAVE_CONVERSATION, imApplication, currentClient, imConversation, "");
        // 遍历发送给已在群内的成员
        List<Long> fkClientIds = chatRoomMembers.keySet().stream().map(key -> Long.valueOf(key.split(RedisUtils.SPLIT)[0])).collect(Collectors.toList());
        List<ImClient> clientReceivers = imClientService.list(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getFkAppid, imApplication.getId()).in(ImClient::getId, fkClientIds));

        for (ImClient receiver : clientReceivers) {
            // 投递消息
            try {
                couriers.deliver(imMessage, null, currentClient, receiver, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
            } catch (Exception e) {
                log.info("下发群会话事件失败，事件类型 {} 接收人 {}", imMessage.getMsgType(), JSON.toJSONString(receiver));
            }
        }
        return true;
    }

    @Override
    public List<ChatRoomMemberVo> listChatRoomMember(ChatRoomMemberPageParam param) {
        Map<String, String> chatRoomMembers = chatRoomCacheManager.findOnlineClientsByChatRoomId(param.getChatRoomId());
        if (chatRoomMembers.isEmpty()) {
            return Lists.newArrayList();
        }
        List<ChatRoomMemberVo> result = Lists.newArrayList();
        for (String key : chatRoomMembers.keySet()) {
            ImClient client = imClientService.getCacheImClient(Long.valueOf(key.split(RedisUtils.SPLIT)[0]));
            result.add(new ChatRoomMemberVo(param.getChatRoomId(), client.getClientId()));
        }
        return result;
    }

    @Override
    public List<ConversationVo> listConversation(ListConversationParam param) {
        if (StringUtils.isBlank(param.getConversationIds())) {
            throw new BusinessException("会话id列表不可为空");
        }
        List<Long> conversationIds = (List) JSON.parse(param.getConversationIds());
        List<ConversationVo> conversationVoList = Lists.newArrayList();
        List<ImConversation> conversations = imConversationMapper.selectList(new QueryWrapper<ImConversation>().lambda().eq(ImConversation::getChatType, param.getChatType()).in(ImConversation::getId, conversationIds).orderByDesc(ImConversation::getId));
        if (CollectionUtils.isEmpty(conversations)) {
            return conversationVoList;
        }
        for (ImConversation conversation : conversations) {
            ConversationVo conversationVo = new ConversationVo();
            BeanUtils.copyProperties(conversation, conversationVo);
            conversationVoList.add(conversationVo);
        }
        return conversationVoList;
    }

    @Override
    public List<ImConversation> getConversationBySenderAndReceivers(Long senderImClientId, Collection<Long> receiverImClientIds) {
        return baseMapper.getConversationBySenderAndReceivers(senderImClientId, receiverImClientIds);
    }

    private ImConversationCreateVo createChatRoom(ImConversationCreate imConversationCreate, ImClient createClient) {
        if (imConversationCreate.getPlatform() == null) {
            throw new BusinessException("平台入参不可为空");
        }
        // 会话id
        Long chatRoomId = SnowflakeUtil.getId();

        // 创建者
        Long creator = createClient.getId();

        // 创建会话
        ImConversation imConversation = new ImConversation();
        imConversation.setId(chatRoomId);
        imConversation.setCreateTime(new Date());
        imConversation.setLastMessage(null);
        imConversation.setFkAppid(createClient.getFkAppid());
        imConversation.setCreator(creator);
        imConversation.setMemberCount(1);
        imConversation.setChatType(imConversationCreate.getChatType());
        imConversation.setName(imConversationCreate.getName());
        imConversation.setSystemFlag(false);
        imConversation.setAttributes(imConversationCreate.getAttributes());
        imConversationService.save(imConversation);

        // 将创建者自己添加到会话
        chatRoomCacheManager.intoRoom(creator, chatRoomId, imConversationCreate.getPlatform());
        ImConversationCreateVo imConversationCreateVo = new ImConversationCreateVo();
        imConversationCreateVo.setId(chatRoomId);

        return imConversationCreateVo;
    }

    private MsgTypeEnum getMsgType(GroupChatSettingTypeEnum type, Boolean on) {
        MsgTypeEnum msgType = null;
        switch (type) {
            case FORBID_ADD_FRIEND:
                if (on) {
                    msgType = CONVERSATION_FORBID_ADD_FRIEND;
                } else {
                    msgType = CONVERSATION_FORBID_ADD_FRIEND_CANCEL;
                }
                break;
            case FORBID_SEND_PIC:
                if (on) {
                    msgType = CONVERSATION_FORBID_SEND_PIC;
                } else {
                    msgType = CONVERSATION_FORBID_SEND_PIC_CANCEL;
                }
                break;
            case FORBID_SEND_LINK:
                if (on) {
                    msgType = CONVERSATION_FORBID_SEND_LINK;
                } else {
                    msgType = CONVERSATION_FORBID_SEND_LINK_CANCEL;
                }
                break;
            case HEAD_PORTRAIT:
                msgType = CONVERSATION_SET_GROUP_PORTRAIT;
                break;
            case NAME:
                msgType = CONVERSATION_NAME_CHANGE;
                break;
            case MUTED:
                if (on) {
                    msgType = CONVERSATION_MUTED;
                } else {
                    msgType = CONVERSATION_MUTED_CANCEL;
                }
                break;
            default:
                throw new BusinessException("设置类型错误");
        }

        return msgType;
    }

    /**
     * 发送会话事件消息至群成员列表
     *
     * @param conversation 会话
     * @param membersList    成员列表
     * @param sender         发送者
     * @param message        消息
     * @Author luozh
     * @Date 2022年04月27日 02:11:19
     */
    @Override
    public void sendMsgToMembers(ImConversation conversation, List<ImConversationMembers> membersList,
                                 ImClient sender, ImMessage message, Object content) {
        Long appId = conversation.getFkAppid();

        List<Long> receiverClientIds = membersList.stream().map(ImConversationMembers::getFkClientId).collect(Collectors.toList());
        List<ImClient> clientReceivers = imClientService.list(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getFkAppid, appId).in(ImClient::getId, receiverClientIds));

        for (ImClient receiver : clientReceivers) {
            // 投递消息
            try {
                couriers.deliver(message, content, sender, receiver, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
            } catch (Exception e) {
                log.info("下发群会话事件失败，事件类型 {} 接收人 {}", message.getMsgType(), JSON.toJSONString(receiver));
            }
        }
    }

    /**
     * 创建私聊会话
     * @Author luozh
     * @Date 2022年05月20日 01:36:33
     * @param
     * @Return
     */
    private ImConversation createPrivateChat(ImClient creator, String memberUserId, String name, String attributes) {
        Long appId = creator.getFkAppid();
        ImClient memberClient = imClientService.getOne(Wrappers.<ImClient>lambdaQuery().eq(ImClient::getClientId,
                memberUserId).eq(ImClient::getFkAppid, appId));

        if (memberClient == null) {
            log.info("成员不存在,不能创建会话 clientId:{}", memberUserId);
            throw new BusinessException(ApiCode.CLIENT_NOT_FOUNT);
        }
        // 查看好友关系
        ImFriend my = friendService.getByKey(creator.getClientId(), memberUserId);
        Boolean isStranger = false;
        if (my == null || !(FriendStateEnum.CONFORM.getCode().equals(my.getState()))) {
            isStranger = true;
        }

        // 如果存在重复单聊类型会话，则不会为空
        ImConversation existConversation = imConversationMapper.getRepetitionConversationSingle(creator.getId(), memberClient.getId());
        if (existConversation != null) {
            // 双方可见
            List<ImConversationMembers> memberList =
                    imConversationMembersService.list(Wrappers.<ImConversationMembers>lambdaQuery().eq(ImConversationMembers::getFkConversationId, existConversation.getId()));
            for (ImConversationMembers members : memberList) {
                members.setDisplayStatus(1L);
            }
            imConversationMembersService.updateBatchById(memberList);

            log.info("存在重复的单聊会话，返回已存在的单聊类型会话id: {}", existConversation.getId());
            // 返回已存在的单聊类型会话id
            return existConversation;
        }
        // 创建会话
        ImConversation conversation = buildConversation(creator, ChatTypeEnum.SINGLE, name, 2, attributes);
        // 创建成员
        List<ImConversationMembers> conversationMemberList = buildPrivateChatMembers(conversation, creator,
                memberClient);
        if (isStranger) {
            // 如果是陌生人，刚创建会话的时候 会话类型设置为临时会话 双方都不可见
            conversation.setChatType(ChatTypeEnum.TEMP.getCode());
            for (ImConversationMembers members : conversationMemberList) {
                members.setDisplayStatus(0L);
            }
        }
        this.save(conversation);
        this.imConversationMembersService.saveBatch(conversationMemberList);

        return conversation;
    }

    /**
     * 创建群聊会话
     * @return
     */
    private ImConversation createGroupChat(ImApplication application, ImClient creator,
                                           List<String> memberUserIds,
                                           String name,
                                           String attributes) {

        if (CollectionUtils.isEmpty(memberUserIds)) {
            log.info("未找到群成员信息");
            throw new BusinessException(ApiCode.PARAMETER_EXCEPTION);
        }
        // 判断client是否都存在
        List<ImClient> memberClients =
                imClientService.list(Wrappers.<ImClient>lambdaQuery()
                        .eq(ImClient::getFkAppid, creator.getFkAppid())
                        .in(ImClient::getClientId, memberUserIds));

        if (memberClients.size() != memberUserIds.size()) {
            memberUserIds.removeAll(memberClients.stream().map(ImClient::getClientId).collect(Collectors.toList()));
            log.info("成员不存在,不能创建会话 clientIds:{}", JSONObject.toJSONString(memberUserIds));
            throw new BusinessException(ApiCode.CLIENT_NOT_FOUNT);
        }

        // 创建会话
        ImConversation conversation = buildConversation(creator, ChatTypeEnum.NORMAL_GROUP, name, memberClients.size() + 1,
                attributes);
        // 创建成员
        List<ImConversationMembers> conversationMemberList = buildGroupChatMembers(conversation, creator,
                memberClients);

        this.save(conversation);
        this.imConversationMembersService.saveBatch(conversationMemberList);

        // 给被拉入群的成员下发事件消息
        for (ImClient member : memberClients) {
            Map<String, Object> content = Maps.newHashMap();
            content.put("operator", creator.getClientId()); //操作的client ID
            content.put("passivityOperator", member.getClientId()); //被操作的client ID
            ImMessage imMessage = MessageBuilder.buildEventMessage(MsgTypeEnum.CLIENT_JOIN_NEW_CONVERSATION, application, creator, conversation, JsonUtils.encodeJson(content));
            imMessageService.save(imMessage);
            // 投递消息
            couriers.deliver(imMessage, content, creator, member, WsResponseCmdEnum.CONVERSATION_EVENT_MSG);
        }

        return conversation;
    }

    private ImConversation buildConversation(ImClient creator, ChatTypeEnum chatType,
                                             String name, Integer memberCount, String attributes) {
        // 创建会话
        Long imConversationId = SnowflakeUtil.getId();
        ImConversation imConversation = new ImConversation();
        imConversation.setId(imConversationId);
        imConversation.setCreateTime(new Date());
        imConversation.setLastMessage(null);
        imConversation.setFkAppid(creator.getFkAppid());
        imConversation.setCreator(creator.getId());
        imConversation.setMemberCount(memberCount);
        imConversation.setChatType(chatType.getCode());
        imConversation.setName(name);
        imConversation.setSystemFlag(false);
        // 拓展数据
        String attributesStr = JsonUtils.encodeJson(attributes);

        imConversation.setAttributes(attributesStr);
        return imConversation;
    }

    /**
     * 构建群聊天成员
     * @Author luozh
     * @Date 2022年05月20日 02:39:32
     * @param conversation
     * @param creator
     * @param memberClient
     * @Return
     */
    private List<ImConversationMembers> buildGroupChatMembers(ImConversation conversation, ImClient creator,
                                                              List<ImClient> memberClient) {
        List<ImConversationMembers> conversationMembers = new ArrayList<>();
        ImConversationMembers creatorMember = buildConversationMember(conversation, creator, GroupRoleEnum.OWNER);
        conversationMembers.add(creatorMember);
        for (ImClient client : memberClient) {
            ImConversationMembers otherMember = buildConversationMember(conversation, client, GroupRoleEnum.NORMAL);
            conversationMembers.add(otherMember);
        }
        return conversationMembers;
    }

    /**
     * 构建私聊成员
     * @Author luozh
     * @Date 2022年05月20日 02:39:43
     * @param conversation
     * @param creator
     * @param memberClient
     * @Return
     */
    private List<ImConversationMembers> buildPrivateChatMembers(ImConversation conversation, ImClient creator,
                                                                ImClient memberClient) {

        List<ImConversationMembers> conversationMembers = new ArrayList<>();
        ImConversationMembers creatorMember = buildConversationMember(conversation, creator, GroupRoleEnum.OWNER);
        ImConversationMembers anotherMember = buildConversationMember(conversation, memberClient, GroupRoleEnum.NORMAL);
        conversationMembers.add(creatorMember);
        conversationMembers.add(anotherMember);

        return conversationMembers;
    }

    /**
     * 构建会话成员
     * @Author luozh
     * @Date 2022年05月20日 02:39:52
     * @param conversation
     * @param member
     * @param role
     * @Return
     */
    private ImConversationMembers buildConversationMember(ImConversation conversation, ImClient member,
                                                          GroupRoleEnum role) {
        Long appId = conversation.getFkAppid();
        Long conversationId = conversation.getId();
        Integer chatType = conversation.getChatType();
        String memberUserId = member.getClientId();
        Date now = new Date();
        Long imConversationMembersId = SnowflakeUtil.getId();
        ImConversationMembers imConversationMembers = new ImConversationMembers();
        imConversationMembers.setId(imConversationMembersId);
        imConversationMembers.setCreateTime(now);
        imConversationMembers.setUpdateTime(now);
        imConversationMembers.setFkAppid(appId);
        imConversationMembers.setFkConversationId(conversationId);
        imConversationMembers.setFkClientId(member.getId());
        imConversationMembers.setClientId(member.getClientId());
        imConversationMembers.setDisplayStatus(1L);
        if (role.equals(GroupRoleEnum.OWNER)) {
            imConversationMembers.setRole(GroupRoleEnum.OWNER.getCode());
            // 会话拥有者加入会话方式固定
            imConversationMembers.setJoinTypeCode(JoinConversationTypeEnum.OWNER.getCode());
            String joinTypeMsg = "";
            if (ChatTypeEnum.SINGLE.getCode().equals(chatType)) {
                joinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + memberUserId + "<@>创建了该会话";
            } else if (ChatTypeEnum.NORMAL_GROUP.getCode().equals(chatType) || ChatTypeEnum.THOUSAND_GROUP.getCode().equals(chatType)) {
                joinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + memberUserId + "<@>创建了该群聊";
            } else if (ChatTypeEnum.CHAT_ROOM.getCode().equals(chatType)) {
                joinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + memberUserId + "<@>创建了该聊天室";
            }
            imConversationMembers.setJoinTypeMsg(joinTypeMsg);
        } else {
            imConversationMembers.setRole(GroupRoleEnum.NORMAL.getCode());
            // 加入会话方式固定为 邀请
            imConversationMembers.setJoinTypeCode(JoinConversationTypeEnum.BE_INVITED.getCode());
            String memberJoinTypeMsg = "";
            if (ChatTypeEnum.SINGLE.getCode().equals(chatType)) {
                memberJoinTypeMsg = "由 <@>" + memberUserId + "<@>邀请加入该会话";
            } else if (ChatTypeEnum.NORMAL_GROUP.getCode().equals(chatType) || ChatTypeEnum.THOUSAND_GROUP.getCode().equals(chatType)) {
                memberJoinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + memberUserId + "<@>邀请进聊";
            } else if (ChatTypeEnum.CHAT_ROOM.getCode().equals(chatType)) {
                memberJoinTypeMsg = DateUtil.format(now, CHINESE_DATE_PATTERN) + " <@>" + memberUserId + "<@>邀请进入聊天室";
            }

            imConversationMembers.setJoinTypeMsg(memberJoinTypeMsg);
        }
        return imConversationMembers;

    }

}
