package com.yonge.cooleshow.biz.dal.service.im.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dayaedu.cbs.common.enums.EClientType;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.microsvc.toolkit.common.tools.ThreadPool;
import com.microsvc.toolkit.middleware.im.ImPluginContext;
import com.microsvc.toolkit.middleware.im.ImPluginService;
import com.microsvc.toolkit.middleware.im.message.GroupMemberWrapper;
import com.microsvc.toolkit.middleware.im.properties.ImConfigProperties;
import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
import com.yonge.cooleshow.biz.dal.entity.ImGroup;
import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
import com.yonge.cooleshow.biz.dal.entity.ImGroupMemberAudit;
import com.yonge.cooleshow.biz.dal.entity.SysUser;
import com.yonge.cooleshow.biz.dal.entity.Teacher;
import com.yonge.cooleshow.biz.dal.enums.AuditStatusEnum;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.RoleEnum;
import com.yonge.cooleshow.biz.dal.enums.im.EImGroupMemberRoleType;
import com.yonge.cooleshow.biz.dal.service.ImGroupMemberAuditService;
import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
import com.yonge.cooleshow.biz.dal.service.ImGroupService;
import com.yonge.cooleshow.biz.dal.service.SysUserService;
import com.yonge.cooleshow.biz.dal.service.im.ImGroupCoreService;
import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupMemberAuditWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupMemberWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
import com.yonge.toolset.base.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * IM群组务
 * Created by Eric.Shang on 2022/11/26.
 */
@Slf4j
@Service
public class ImGroupCoreServiceImpl implements ImGroupCoreService {

    @Autowired
    private ImPluginContext imPluginContext;
    @Autowired
    private ImGroupService imGroupService;
    @Autowired
    private ImConfigProperties imConfig;
    @Autowired
    private ImGroupMemberService imGroupMemberService;
    @Autowired
    private ImGroupMemberAuditService imGroupMemberAuditService;
    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private TeacherDao teacherDao;

    private static final String IM_USER_ID_SPLIT = "_";


    /**
     * IM 用户注册
     *
     * @param userId   用户ID
     * @param username 用户名
     * @param avatar   用户头象
     * @return IM聊天Token
     */
    @Override
    public ImGroupWrapper.ImUserInfo register(String userId,String clientType, String username, String avatar) throws Exception {

        ImGroupWrapper.ImUserInfo userInfo = ImGroupWrapper.ImUserInfo.builder()
                .imUserId("").imToken("").build();
        // 生成IM聊天Token
        try {

            // 聊天Token
            String imUserId = getImUserId(userId,clientType);

            // 生成签名
            userInfo.imUserId(imUserId)
                    .setImToken(imPluginContext.getPluginService().register(imUserId, username, avatar));

        } catch (Exception e) {
            log.error("register ImToken EX, userId={}, username={}", userId, username, e);
        }
        return userInfo;
    }

    /**
     * IM 用户注册
     *
     * @param userId 用户Id
     * @return String
     */
    @Override
    public String getImUserId(String userId, String clientType) {
        String imUserId = userId;
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !userId.startsWith(imConfig.getAppPrefix())) {
            imUserId = MessageFormat.format("{0}_{1}_{2}", imConfig.getAppPrefix(), userId, clientType);
        }
        return imUserId;
    }

    @Override
    public String getImUserId(Long userId, RoleEnum userRole) {
        String imUserId = String.valueOf(userId);
        String clientType = "STUDENT";
        if(userRole.RoleTeacher == userRole){
            clientType = "TEACHER";
        }
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !imUserId.startsWith(imConfig.getAppPrefix())) {
            imUserId = MessageFormat.format("{0}_{1}_{2}", imConfig.getAppPrefix(), userId.toString(), clientType);
        }
        return imUserId;
    }

    @Override
    public String getImUserId(Long userId, ClientEnum clientEnum) {
        String imUserId = String.valueOf(userId);
        String clientType = "STUDENT";
        if(clientEnum.TEACHER == clientEnum){
            clientType = "TEACHER";
        }
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !imUserId.startsWith(imConfig.getAppPrefix())) {
            imUserId = MessageFormat.format("{0}_{1}_{2}", imConfig.getAppPrefix(), userId.toString(), clientType);
        }
        return imUserId;
    }


    /**
     * 解析IM用户规则
     *
     * @param imUserId IM用户Id
     * @return String
     */
    @Override
    public String analysisImUserId(String imUserId) {
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && imUserId.startsWith(imConfig.getAppPrefix())) {
            return imUserId.replace(imConfig.getAppPrefix() + "_", "").split("_")[0];
        }
        return imUserId;
    }
    @Override
    public String analysisImUser(String imUserId) {
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && imUserId.startsWith(imConfig.getAppPrefix())) {
            return imUserId.replace(imConfig.getAppPrefix() + "_", "");
        }
        return imUserId;
    }


    /**
     * 检测imUserid
     *
     * @param imUserId IM用户Id
     * @return String
     */
    @Override
    public boolean checkImUserId(String imUserId) {
        return imUserId.startsWith(imConfig.getAppPrefix());
    }

    @Override
    public String analysisImUserClient(String imUserId) {
        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && imUserId.startsWith(imConfig.getAppPrefix())) {
            imUserId =  imUserId.replace(imConfig.getAppPrefix() + "_", "");
            String[] s = imUserId.split("_");
            if (s.length > 1) {
                return imUserId.replace(s[0] + "_", "");
            }
        }
        return imUserId;
    }

    /**
     * 添加群组信息
     *
     * @param userInfo 用户信息
     * @param imGroup  ImGroupWrapper.ImGroup
     * @return Boolean
     */
    @Override
    public String groupCreate(GroupMemberWrapper.ImGroupMember userInfo, ImGroupWrapper.ImGroup imGroup) throws Exception {

        long id = Optional.ofNullable(imGroup.getId()).orElse(IdWorker.getId());
        // 生成群组ID
        imGroup.id(id)
                .type(Optional.ofNullable(imGroup.getType()).orElse("CLASS"))
                .groupId(String.valueOf(id));

        ImGroup group = JSON.parseObject(imGroup.jsonString(), ImGroup.class);
        group.setId(imGroup.getGroupId());
        // 创建群默认全员不禁言
        group.setConfigJson(ImGroupWrapper.ImGroupConfigJson.builder().mute(false).build().toJson());
        // 保存群组信息
        imGroupService.saveOrUpdate(group);


        List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
        //记录群成员
        ImGroupMemberWrapper.ImGroupMember groupMember = ImGroupMemberWrapper.ImGroupMember
                .builder()
                .id(IdWorker.getId())
                .groupId(imGroup.getGroupId())
                .userId(userInfo.getUserId())
                .clientType(userInfo.getClientType())
                .avatar(userInfo.getAvatar())
                .nickname(userInfo.getNickname())
                .isAdmin(true)
                .roleType(userInfo.getRoleType())
                .build();

        groupMembers.add(groupMember.imUserId(getImUserId(groupMember.getUserId().toString(),userInfo.getClientType())));
        imGroupMemberService.insertBatch(JSON.parseArray(JSON.toJSONString(groupMembers), ImGroupMember.class));

        // 自动激活学生IM帐号
        registerUser(groupMembers);

        // 创建IM群组
        imPluginContext.getPluginService().groupCreate(imGroup.getGroupId(), imGroup.getName(),imGroup.getImg(),
                getImUserId( userInfo.getUserId().toString(), userInfo.getClientType()));

        // 添加群成员到当前群组
        imPluginContext.getPluginService().groupJoin(imGroup.getGroupId(), imGroup.getName(),
                getImGroupMembers(groupMembers));

        return imGroup.getGroupId();
    }

    /**
     * 获取群组成员信息
     *
     * @param groupMembers 群组成员信息
     * @return List<GroupMemberWrapper.ImGroupMember>
     */
    private List<GroupMemberWrapper.ImGroupMember> getImGroupMembers(List<ImGroupMemberWrapper.ImGroupMember> groupMembers) {
        // 统一转换为群组成员信息
        for (ImGroupMemberWrapper.ImGroupMember member : groupMembers) {
            member.setImUserId(getImUserId(member.getUserId().toString(),member.getClientType()));
        }
        return JSON.parseArray(JSON.toJSONString(groupMembers), GroupMemberWrapper.ImGroupMember.class);
    }

    /**
     * 自动注册用户
     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
     */
    private void asyncRegisterUser(List<ImGroupMemberWrapper.ImGroupMember> groupMembers) {
        ThreadPool.getExecutor().submit(() -> registerUser(groupMembers));
    }

    /**
     * 批量导入用户
     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
     */
    private void registerUser(List<ImGroupMemberWrapper.ImGroupMember> groupMembers) {
        for (ImGroupMemberWrapper.ImGroupMember member : groupMembers) {
            // 激活用户帐号为空
            if (StringUtils.isBlank(member.getNickname())) {
                continue;
            }

            try {
                String imUserId = member.getUserId().toString();
                if (StringUtils.isBlank(imUserId)) {
                    imUserId = getImUserId(String.valueOf(member.getUserId()),
                            member.getClientType());
                }

                imPluginContext.getPluginService().register(imUserId, member.getNickname(), member.getAvatar());
            } catch (Exception e) {
                log.error("registerUser member imToken");
            }
        }
    }

    /**
     * 添加群组信息
     *
     * @param imGroups ImGroupWrapper.ImGroup
     * @throws Exception Exception
     */
    @Override
    public void groupCreate(List<ImGroupWrapper.ImGroup> imGroups) throws Exception {

        if (CollectionUtils.isEmpty(imGroups)) {
            return;
        }

        for (ImGroupWrapper.ImGroup item : imGroups) {

            GroupMemberWrapper.ImGroupMember groupMember = GroupMemberWrapper.ImGroupMember.builder()
                    .userId(item.getCreateBy())
                    .roleType(ClientEnum.TEACHER.getCode())
                    .clientType(ClientEnum.TEACHER.getCode())
                    .build();
            // 添加群组信息
            groupCreate(groupMember, item);
        }
    }


    /**
     * 群组解散
     *
     * @param groupId  群组ID
     */
    @Override
    public void groupDismiss(String groupId) throws Exception {

        // 判定登录用户是否为群主
        ImGroup imGroup = imGroupService.lambdaQuery().eq(ImGroup::getId, groupId).last("LIMIT 1").one();
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 删除入群申请
        LambdaQueryWrapper<ImGroupMemberAudit> auditQueryWrapper = Wrappers.<ImGroupMemberAudit>lambdaQuery()
                .eq(ImGroupMemberAudit::getGroupId, groupId);
        imGroupMemberAuditService.remove(auditQueryWrapper);

        // 获取群成员
        List<ImGroupMemberWrapper.ImGroupMember> groupMembers = imGroupMemberService.lambdaQuery()
                .select(ImGroupMember::getUserId, ImGroupMember::getGroupId, ImGroupMember::getRoleType)
                .eq(ImGroupMember::getGroupId, groupId)
                .list().stream()
                .map(x -> ImGroupMemberWrapper.ImGroupMember.builder().userId(x.getUserId()).groupId(x.getGroupId()).clientType(x.getRoleType().getCode()).build())
                .collect(Collectors.toList());

        // 删除群组
        imGroupService.remove(Wrappers.<ImGroup>lambdaQuery().eq(ImGroup::getId, groupId));

        // 删除群组成员
        imGroupMemberService.remove(Wrappers.<ImGroupMember>lambdaQuery().eq(ImGroupMember::getGroupId, groupId));

        // 解散群
        imPluginContext.getPluginService().groupDismiss(imGroup.getId(), getImGroupMembers(groupMembers));

    }

    /**
     * 用户主动退出群聊
     *
     * @param imGroupMember 用户信息
     * @param groupId  群组ID
     * @throws Exception Exception
     */
    @Override
    public void groupQuit(GroupMemberWrapper.ImGroupMember imGroupMember, String groupId,Boolean quit) throws Exception {

        // 判定登录用户是否为群主
        ImGroup imGroup = imGroupService.lambdaQuery().eq(ImGroup::getId, groupId).last("LIMIT 1").one();
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 统计当前群成员用户数
        long count = imGroupMemberService.count(Wrappers.<ImGroupMember>lambdaQuery().eq(ImGroupMember::getGroupId, groupId));

        // 删除当前用户群成员身份
        boolean remove = imGroupMemberService.remove(Wrappers.<ImGroupMember>lambdaQuery()
                .eq(ImGroupMember::getGroupId, groupId)
                .eq(ImGroupMember::getUserId, imGroupMember.getUserId())
                .eq(ImGroupMember::getRoleType, imGroupMember.getRoleType())
        );
        if (!remove) {
            // 用户主动退出群聊失败
            throw new BizException("用户主动退出群聊失败");
        }

        // 修改群组成员数
        imGroupService.lambdaUpdate()
                .eq(ImGroup::getId, imGroup.getId())
                .set(ImGroup::getMemberNum, count - 1)
                .update();

        // 群主退出
        List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
        groupMembers.add(ImGroupMemberWrapper.ImGroupMember.builder()
                .userId(imGroupMember.getUserId())
                .groupId(groupId)
                .clientType(imGroupMember.getClientType())
                .build());

        if (Optional.ofNullable(quit).orElse(true)) {
            imPluginContext.getPluginService().groupQuit(imGroup.getId(), getImGroupMembers(groupMembers));
        }
    }

    @Override
    public void  accountDelete(List<String> imUserIds) {
        try {
            imPluginContext.getPluginService().accountDelete(imUserIds);
        } catch (Exception e) {
            log.error("accountDelete error",e);

        }

    }


    @Override
    public void  messageDelete(String imUserId,List<String> toImUserIds) {
        if (CollectionUtils.isEmpty(toImUserIds)) {
            return;
        }

        for (String toImUserId : toImUserIds) {
            try {
                imPluginContext.getPluginService().deletePrivateRecentContact(imUserId,null);
            } catch (Exception e) {
                log.error("messageDelete error",e);
            }

        }

    }

    /**
     * 用户主动退出群聊
     *
     * @param userId     用户信息
     * @param clientType EClientType
     * @param imGroupId  群组ID
     * @throws Exception Exception
     */
    @Override
    public void groupQuit(Long userId, String clientType, String imGroupId ,Boolean quit) throws Exception {

        ImGroup imGroup = imGroupService.getById(imGroupId);
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        GroupMemberWrapper.ImGroupMember groupMember = GroupMemberWrapper.ImGroupMember.builder()
                .userId(userId)
                .roleType(clientType)
                .clientType(clientType)
                .build();
        // 退出群聊
        groupQuit(groupMember, imGroup.getId(), quit);
    }

    /**
     * 入群申请消息
     *
     * @param imGroupMember    JwtUserInfo<?>
     * @param memberAudit ImGroupMemberAuditWrapper.ImGroupMemberAudit
     */
    @Override
    public void groupJoinApply(GroupMemberWrapper.ImGroupMember imGroupMember, ImGroupMemberAuditWrapper.ImGroupMemberAudit memberAudit) throws Exception {

        // 判断群ID是否有效
        ImGroup imGroup = imGroupService.lambdaQuery().eq(ImGroup::getId, memberAudit.getGroupId()).last("LIMIT 1").one();
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 判断用户是否已入群
        int exists = imGroupMemberService.lambdaQuery()
                .eq(ImGroupMember::getGroupId, memberAudit.getGroupId())
                .eq(ImGroupMember::getUserId, memberAudit.getUserId())
                .eq(ImGroupMember::getRoleType, memberAudit.getRoleType())
                .count();
        if (exists > 0) {
            throw new BizException("用户已入群");
        }

        // 是否有待审核消息
        exists = imGroupMemberAuditService.lambdaQuery()
                .eq(ImGroupMemberAudit::getGroupId, memberAudit.getGroupId())
                .eq(ImGroupMemberAudit::getUserId, memberAudit.getUserId())
                .eq(ImGroupMemberAudit::getRoleType, memberAudit.getRoleType())
                .eq(ImGroupMemberAudit::getAuditStatus, AuditStatusEnum.AUDITING)
                .count();
        if (exists >0) {
            throw new BizException("已有待审核消息");
        }

        // 保存入群申请消息
        imGroupMemberAuditService.save(JSON.parseObject(JSON.toJSONString(memberAudit), ImGroupMemberAudit.class));


        // 群是否自动通过审核
        if (Optional.ofNullable(imGroup.getAutoPassFlag()).orElse(false)) {

            // 自动加群
            List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
            //记录群成员
            ImGroupMemberWrapper.ImGroupMember groupMember = ImGroupMemberWrapper.ImGroupMember
                    .builder()
                    .groupId(imGroup.getId())
                    .userId(memberAudit.getUserId())
                    .clientType(memberAudit.getClientType())
                    .avatar(imGroupMember.getAvatar())
                    .nickname(imGroupMember.getNickname())
                    .isAdmin(false)
                    .roleType(memberAudit.getRoleType())
                    .build();


            groupMembers.add(groupMember.imUserId(getImUserId(groupMember.getUserId().toString(),groupMember.getClientType())));
            saveImGroupMemberList(imGroup.getId(), groupMembers);

        }
    }

    /**
     * 添加群组成员
     * @param imGroupId 群组ID
     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
     * @throws Exception Exception
     */
    @Override
    public void saveImGroupMemberList(String imGroupId, List<ImGroupMemberWrapper.ImGroupMember> groupMembers) throws Exception {

        if (CollectionUtils.isEmpty(groupMembers)) {
            // 群成员为空，直接忽略
            return;
        }
        // 查询群组主键Id
        for (ImGroupMemberWrapper.ImGroupMember item : groupMembers) {
            item.id(IdWorker.getId()).imUserId(getImUserId(item.getUserId().toString(), item.getClientType())).setGroupId(imGroupId);
        }

        imGroupMemberService.insertBatch(JSON.parseArray(JSON.toJSONString(groupMembers), ImGroupMember.class));
        // 判断群ID是否有效
        ImGroup imGroup = imGroupService.lambdaQuery().eq(ImGroup::getId, imGroupId).last("LIMIT 1").one();
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }
        // 更新群组成员数
        updateGroupMemberNumber(imGroup.getId(), imGroup);

        if (groupMembers.stream().anyMatch(ImGroupMemberWrapper.ImGroupMember::getGroupJoin)) {

            // 过滤出需要添加到群组的成员
            groupMembers = groupMembers.stream().filter(ImGroupMemberWrapper.ImGroupMember::getGroupJoin).collect(Collectors.toList());

            // 添加群成员到当前群组
            imPluginContext.getPluginService().groupJoin(imGroup.getId(), imGroup.getName(), getImGroupMembers(groupMembers));

            // 自动激活用户
            asyncRegisterUser(groupMembers);
        }

        // 刷新群成员禁言状态
        flushGroupMute(imGroup.getId());
    }

    /**
     * 添加群组成员
     *
     * @param imGroupId    群组主键ID
     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
     * @throws Exception Exception
     */
    @Override
    public void saveImGroupMemberList(Long imGroupId, List<ImGroupMemberWrapper.ImGroupMember> groupMembers) throws Exception {

        // 群组信息
        ImGroup imGroup = imGroupService.getById(imGroupId);
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 添加群组成员信息
        saveImGroupMemberList(imGroup.getId(), groupMembers);
    }

    /**
     * 群成员加入
     *
     * @param imGroupId    群主ID
     * @param groupMembers 群成员
     * @throws Exception Exception
     */
    @Override
    public void groupMemberJoin(String imGroupId, List<ImGroupMemberWrapper.ImGroupMember> groupMembers) throws Exception {

        // 群组信息
        ImGroup imGroup = imGroupService.lambdaQuery().eq(ImGroup::getId, imGroupId).last("LIMIT 1").one();
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 添加群组成员信息
        if (CollectionUtils.isEmpty(groupMembers)) {
            // 群成员为空，直接忽略
            return;
        }

        // 添加群成员到当前群组
        imPluginContext.getPluginService().groupJoin(imGroup.getId(), imGroup.getName(), getImGroupMembers(groupMembers));

        // 自动激活用户
        asyncRegisterUser(groupMembers);

        // 刷新群成员禁言状态
        flushGroupMute(imGroup.getId());

    }

    private void flushGroupMute(String groupId) {
        try {
            List<GroupMemberWrapper.Member> members = imPluginContext.getPluginService().groupUserGagList(groupId);

            List<ImGroupMember> list = imGroupMemberService.lambdaQuery()
                    .eq(ImGroupMember::getGroupId, groupId)
                    .list();
            if (CollectionUtils.isEmpty(members)) {
                // 设置为全体非禁言状态
                for (ImGroupMember item : list) {
                    item.setGroupMute(false);
                }

                return;
            } else {
                List<Long> userIds = members.stream().map(o -> analysisImUserId(o.getId())).map(Long::valueOf).collect(Collectors.toList());
                list = list.stream().map(o -> {
                    if (userIds.contains(o.getUserId())) {
                        o.setGroupMute(true);
                    } else {
                        o.setGroupMute(false);
                    }
                    return o;
                }).collect(Collectors.toList());
            }

            if (CollectionUtils.isNotEmpty(list)) {
                imGroupMemberService.updateBatchById(list);
            }
        } catch (Exception e) {
            log.error("flushGroupMute error",e);
        }

    }

    /**
     * 更新群组成员数
     * @param groupId 群组ID
     * @param imGroup ImGroup
     */
    private void updateGroupMemberNumber(String groupId, ImGroup imGroup) {

        // 统计当前群成员用户数
        long count = imGroupMemberService.count(Wrappers.<ImGroupMember>lambdaQuery().eq(ImGroupMember::getGroupId, groupId));

        // 修改群组成员数
        imGroupService.lambdaUpdate()
            .eq(ImGroup::getId, imGroup.getId())
            .set(ImGroup::getMemberNum, count)
            .update();
    }

    /**
     * 入群申请审核
     *
     * @param imGroupMember JwtUserInfo<?>
     * @param audit    ImGroupMemberAuditWrapper.ApplyBatchAudit
     */
    @Override
    public void groupJoinAudit(GroupMemberWrapper.ImGroupMember imGroupMember, ImGroupMemberAuditWrapper.ApplyBatchAudit audit) throws Exception {

        Collection<ImGroupMemberAudit> memberAudits = imGroupMemberAuditService.listByIds(audit.getBatchAuditIds());
        // 判断申请审核状态是否匹配
        boolean anyMatch = memberAudits.stream().anyMatch(x -> x.getAuditStatus() != AuditStatusEnum.AUDITING);

        if (CollectionUtils.isEmpty(memberAudits) || anyMatch) {
            throw new BizException("审核状态不匹配");
        }

        ImGroup imGroup = imGroupService.getById(audit.getGroupId());
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        // 非群主用户，不能进行审核
        if (!imGroupMember.getUserId().equals(imGroup.getCreateBy())) {
            throw new BizException("非群主用户，不能进行审核");
        }

        // 批量更新入群申请状态
        imGroupMemberAuditService.lambdaUpdate()
                .in(ImGroupMemberAudit::getId, audit.getBatchAuditIds())
                .eq(ImGroupMemberAudit::getGroupId, audit.getGroupId())
                .set(ImGroupMemberAudit::getAuditStatus, audit.getAuditStatus())
                .update();

        // 申请通过
        if (AuditStatusEnum.OPEN.getCode().equals(audit.getAuditStatus())) {

            // 添加群成员
            List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
            for (ImGroupMemberAudit item : memberAudits) {

                //记录群成员

                ImGroupMemberWrapper.ImGroupMember groupMember = ImGroupMemberWrapper.ImGroupMember
                        .builder()
                        .groupId(imGroup.getId())
                        .userId(item.getUserId())
                        .clientType(item.getRoleType().getCode())
                        .imUserId(getImUserId(item.getUserId().toString(),item.getRoleType().getCode()))
                        .avatar("")
                        .nickname("")
                        .isAdmin(false)
                        .roleType(item.getRoleType().getCode())
                        .build();
                groupMembers.add(groupMember);
            }
            imGroupMemberService.insertBatch(JSON.parseArray(JSON.toJSONString(groupMembers), ImGroupMember.class));

            // 更新群组成员数
            updateGroupMemberNumber(imGroup.getId(), imGroup);

            // 添加群成员到当前群组
            imPluginContext.getPluginService().groupJoin(imGroup.getId(), imGroup.getName(), getImGroupMembers(groupMembers));

            // 自动激活用户
            asyncRegisterUser(groupMembers);
        }
    }

    /**
     * 添加进入群组
     *
     * @param userId     用户ID
     * @param clientType 客户端类型
     * @param imGroupId  群组ID
     */
    @Override
    public void groupJoin(Long userId, String clientType, Long imGroupId) throws Exception {

        // 群组信息
        ImGroup imGroup = imGroupService.getById(imGroupId);
        if (Objects.isNull(imGroup)) {
            throw new BizException("群组不存在");
        }

        List<ImGroupMemberWrapper.ImGroupMember> imGroupMembers = new ArrayList<>();
        ImGroupMemberWrapper.ImGroupMember groupMember = ImGroupMemberWrapper.ImGroupMember
                .builder()
                .groupId(imGroup.getId())
                .userId(userId)
                .clientType(clientType)
                .imUserId(getImUserId(userId.toString(),clientType))
                .isAdmin(false)
                .roleType(clientType)
                .build();
        imGroupMembers.add(groupMember);

        saveImGroupMemberList(imGroupId, imGroupMembers);
    }

    /**
     * 转让群主
     *
     * @param groupId  群组ID
     * @param newOwner 新群主
     * @param oldOwner 旧群主
     */
    @Override
    public void changeGroupOwner(String groupId, String newOwner, String oldOwner) throws Exception {
        try {
            if (newOwner.equals(oldOwner)) {
                throw new BizException("不能转让给自己");
            }

            {
                // 判断旧群主
                String[] values = analysisImUser(oldOwner).split(IM_USER_ID_SPLIT);

                // 新群主ID和身份
                long userId = Long.parseLong(values[0]);
                String clientType = values[1];
                ImGroupMember groupMember = imGroupMemberService.lambdaQuery()
                        .eq(ImGroupMember::getGroupId, groupId)
                        .eq(ImGroupMember::getUserId, userId)
                        .eq(ImGroupMember::getRoleType, clientType)
                        .eq(ImGroupMember::getIsAdmin, true)
                        .last("LIMIT 1")
                        .one();
                if (groupMember == null) {
                    throw new BizException("旧群主错误");
                }
            }

            {
                // 判定新群主是否为禁言状态，需要先解除禁言
                String[] values = analysisImUser(newOwner).split(IM_USER_ID_SPLIT);

                // 新群主ID和身份
                long userId = Long.parseLong(values[0]);
                String clientType = values[1];

                ImGroupMember member = imGroupMemberService.lambdaQuery()
                        .eq(ImGroupMember::getGroupId, groupId)
                        .eq(ImGroupMember::getUserId, userId)
                        .eq(ImGroupMember::getRoleType, clientType)
                        .last("LIMIT 1")
                        .one();
                if (Objects.isNull(member)) {
                    throw new BizException("新群主用户不存在");
                }
                // 解除禁言
                if (Optional.ofNullable(member.getGroupMute()).orElse(false)) {
                    imPluginContext.getPluginService().groupUserGagRemove(getImUserId(newOwner, clientType), groupId);

                    // 更新用户群禁言状态
                    imGroupMemberService.lambdaUpdate()
                            .eq(ImGroupMember::getId, member.getId())
                            .set(ImGroupMember::getGroupMute, false)
                            .update();
                }
            }
            // 转主群主
            imPluginContext.getPluginService().changeGroupOwner(groupId, newOwner, oldOwner);

            // 修改群成员身份
            updateGroupOwner(groupId, newOwner, true);

            // 旧群主ID和身份
            updateGroupOwner(groupId, oldOwner, false);

        } catch (Exception e) {
            log.error("changeGroupOwner 转让群主失败, groupId={}, newOwner={}, oldOwner={}", groupId, newOwner, oldOwner, e);
            throw new BizException("转让群主失败");
        }

        // 转主群主
//        imPluginContext.getPluginService().changeGroupOwner(groupId, getImUserId(newOwner,ClientEnum.TEACHER.getCode()), getImUserId(oldOwner,ClientEnum.TEACHER.getCode()));
    }

    /**
     * 更新群信息
     *
     * @param groupId   群ID
     * @param groupName 群名称
     * @param faceUrl   群头像
     * @throws Exception Exception
     */
    @Override
    public void modifyGroupInfo(String groupId, String groupName, String faceUrl) throws Exception {

        // 更新群信息
        imPluginContext.getPluginService().modifyGroupInfo(groupId, groupName, faceUrl);
    }

    /**
     * 群成员加入三方IM状态
     *
     * @param groupMemberIds 群成员ID
     * @param groupJoin      加群状态
     */
    @Override
    public void updateGroupMemberJoinStatus(List<Long> groupMemberIds, Boolean groupJoin) {

//        if (CollectionUtils.isEmpty(groupMemberIds)) {
//            return;
//        }
//
//        // 群成员加入三方IM状态
//        imGroupMemberService.lambdaUpdate()
//                .set(ImGroupMember::getGroupJoin, groupJoin)
//                .in(ImGroupMember::getId, groupMemberIds)
//                .update();
    }

    /**
     * 删除最近私聊消息
     *
     * @param userId   发送用户ID
     * @param toUserId 接收用户ID
     * @return Boolean
     * @throws Exception Exception
     */
    @Override
    public Boolean deletePrivateRecentContact(String userId, String toUserId) throws Exception {
        return imPluginContext.getPluginService().deletePrivateRecentContact(userId, toUserId);
    }

    /**
     * 删除最近群聊消息
     *
     * @param userId  发送用户ID
     * @param groupId 群组ID
     * @return Boolean
     * @throws Exception Exception
     */
    @Override
    public Boolean deleteGroupRecentContact(String userId, String groupId) throws Exception {
        return imPluginContext.getPluginService().deleteGroupRecentContact(userId, groupId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void groupMemberMute(ImGroupMemberWrapper.GroupMemberMute member) {
        try {
            // 群成员ID
            List<Long> userIds = JSON.parseArray(JSON.toJSONString(member.getUserIds()), Long.class);


            // 更新群成员禁言状态
            List<ImGroupMember> list = imGroupMemberService.lambdaQuery()
                    .eq(ImGroupMember::getGroupId, member.getGroupId())
                    .eq(ImGroupMember::getGroupRoleType, member.getGroupRoleType())
                    .in(ImGroupMember::getUserId, userIds)
                    .list();
            if (CollectionUtils.isEmpty(list)) {
                return;
            }


            // 用户ImUserId
            List<String> imUserIds = Lists.newArrayList();
            List<ImGroupMember> updateList = new ArrayList<>();
            for (ImGroupMember imGroupMember : list) {
                imUserIds.add(getImUserId(imGroupMember.getUserId().toString(), imGroupMember.getRoleType().getCode()));
                ImGroupMember imGroupMember1 = new ImGroupMember();
                imGroupMember1.setId(imGroupMember.getId());
                imGroupMember1.setGroupMute(member.getGroupMute());
                updateList.add(imGroupMember1);
            }
            imGroupMemberService.updateBatchById(updateList);

            ImPluginService pluginService = imPluginContext.getPluginService();
            if (Boolean.TRUE.equals(member.getGroupMute())) {
                // 群成员禁言
                pluginService.groupUserGagCreate(imUserIds, member.getGroupId(), member.getMuteTime());
            } else {
                // 群成员解除禁言
                pluginService.groupUserGagRemove(imUserIds, member.getGroupId());
            }

        } catch (Exception e) {
            log.error("群成员禁言失败", e);
            throw new BizException("群成员禁言失败，请联系管理员!");
        }
    }

    @Override
    public IPage<ImGroupMemberWrapper.ImGroupMember> imGroupMemberPage(IPage<ImGroupMemberWrapper.ImGroupMember> page, ImGroupMemberWrapper.ImGroupMemberQuery query) {
        // 群聊成员列表
        IPage<ImGroupMemberWrapper.ImGroupMember> pages = imGroupMemberService.selectPage(page, query);
        if (CollectionUtils.isNotEmpty(pages.getRecords())) {
            List<ImGroupMemberWrapper.ImGroupMember> records = pages.getRecords();

            List<Long> userIds = records.stream().map(ImGroupMemberWrapper.ImGroupMember::getUserId).distinct().collect(Collectors.toList());
            Map<Long, SysUser> userIdMap = sysUserService.getMapByIds(userIds);

            // 填充群成员信息
            for (ImGroupMemberWrapper.ImGroupMember item : records) {
                item.setImUserId(getImUserId(item.getImUserId(), item.getClientType()));
                if (userIdMap.containsKey(item.getUserId())) {
                    SysUser sysUser = userIdMap.get(item.getUserId());
                    item.setAvatar(sysUser.getAvatar());
                    item.setNickname(sysUser.getUsername());
                }
            }
        }
        return pages;
    }

    @Override
    public void groupMemberRole(ImGroupMemberWrapper.GroupMemberRole member) {
        try {
            // 群成员ID
            List<Long> userIds = JSON.parseArray(JSON.toJSONString(member.getUserIds()), Long.class);

            // 更新群成员禁言状态
            List<ImGroupMember> list = imGroupMemberService.lambdaQuery()
                    .eq(ImGroupMember::getGroupId, member.getGroupId())
                    .in(ImGroupMember::getUserId, userIds)
                    .list();
            if (CollectionUtils.isEmpty(list)) {
                return;
            }


            // 用户ImUserId
            List<String> imUserIds = Lists.newArrayList();
            List<ImGroupMember> updateList = new ArrayList<>();
            for (ImGroupMember imGroupMember : list) {
                imUserIds.add(getImUserId(imGroupMember.getUserId().toString(), imGroupMember.getRoleType().getCode()));
                ImGroupMember imGroupMember1 = new ImGroupMember();
                imGroupMember1.setId(imGroupMember.getId());
                imGroupMember1.setGroupRoleType(member.getGroupRoleType());
                updateList.add(imGroupMember1);
            }
            imGroupMemberService.updateBatchById(updateList);

            for (List<ImGroupMember> items : Lists.partition(list, 50)) {

                items.parallelStream().forEach(item -> {

                    String imUserId = getImUserId(item.getUserId().toString(), item.getRoleType().getCode());
                    try {
                        ImPluginService pluginService = imPluginContext.getPluginService();
                        switch (member.getGroupRoleType()) {
                            case Admin:
                                // 管理员取消群禁言状态
                                if (Optional.ofNullable(item.getGroupMute()).orElse(false)) {
                                    pluginService.groupUserGagRemove(imUserId, member.getGroupId());

                                    imGroupMemberService.lambdaUpdate()
                                            .eq(ImGroupMember::getId, item.getId())
                                            .set(ImGroupMember::getGroupMute, false)
                                            .update();
                                }

                                // 设置管理员
                                pluginService.groupAdminCreate(member.getGroupId(), imUserId);
                                break;
                            case Member:
                                // 取消管理员
                                pluginService.groupAdminRemove(member.getGroupId(), imUserId);
                                break;
                            default:
                                break;
                        }
                    } catch (Exception e) {
                        log.error("群成员身份更新异常", e);
                        throw new BizException("群成员身份更新异常，请联系管理员!");
                    }
                });
            }

        } catch (Exception e) {
            log.error("群成员身份更新异常", e);
            throw new BizException("群成员身份更新异常，请联系管理员!");
        }
    }

    /**
     * 更新用户群主身份
     * @param groupId 群组ID
     * @param groupMember 群成员
     * @param isAdmin 群主标记
     */
    private void updateGroupOwner(String groupId, String groupMember, Boolean isAdmin) {
        String[] values = analysisImUser(groupMember).split(IM_USER_ID_SPLIT);

        // 新群主ID和身份
        long userId = Long.parseLong(values[0]);
        String clientType = values[1];

        EImGroupMemberRoleType groupRoleType = EImGroupMemberRoleType.fromClientType(clientType);
        if (Boolean.TRUE.equals(isAdmin)) {
            groupRoleType = EImGroupMemberRoleType.Owner;
        }

        imGroupMemberService.lambdaUpdate()
                .eq(ImGroupMember::getGroupId, groupId)
                .eq(ImGroupMember::getUserId, userId)
                .eq(ImGroupMember::getRoleType, EClientType.valueOf(clientType))
                .set(ImGroupMember::getGroupRoleType, groupRoleType)
                .set(ImGroupMember::getIsAdmin, isAdmin)
                .update();

        if (EImGroupMemberRoleType.Admin == groupRoleType) {
            // 设置管理员
            try {
                imPluginContext.getPluginService().groupAdminCreate(groupId, groupMember);
            } catch (Exception e) {

                log.error("updateGroupOwner 设置管理员失败, groupId={}, groupMember={}", groupId, groupMember, e);
            }
        }
    }

    /**
     * 获取系统随机默认客服
     *
     * @return String
     */
    @Override
    public String getSysRandomCustomerService() {

        // 获取系统客服列表
        List<Teacher> teachers = teacherDao.selectCustomerServiceV2();
        if (CollectionUtils.isEmpty(teachers)) {
            return "";
        }

        // IM客服ID列表
        List<String> customerServiceIds = teachers.stream()
            .map(Teacher::getImCustomerId)
            .map(x -> {
                String[] splits = x.split("_");
                return getImUserId(splits[0], splits[1]);
            }).collect(Collectors.toList());

        // 获取客服关联用户数量
        Map<String, Integer> collect = teacherDao.countCustomerServiceMemberNum().stream()
            .collect(Collectors.toMap(StatGroupWrapper::getGid, StatGroupWrapper::getTotal, (o, n) -> n));

        Map<String, Integer> customerServiceMap = Maps.newHashMap();
        for (String item : customerServiceIds) {
            customerServiceMap.put(item, collect.getOrDefault(item, 0));
        }

        // 按客服好友数量分组
        Map<Integer, List<String>> group = customerServiceMap.entrySet().stream()
            .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

        // 客服好友数按升序排序，获取一个客服
        List<String> customerServiceList = group.entrySet().stream()
            .sorted(Comparator.comparingInt(Map.Entry::getKey))
            .map(Map.Entry::getValue)
            .findFirst().orElse(Lists.newArrayList());

        if (CollectionUtils.isEmpty(customerServiceList)) {
            return "";
        }
        // 获取一个客服
        return customerServiceList.get(0);
    }
}
