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

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.microsvc.toolkit.common.webportal.exception.BizException;
import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.auth.config.CustomerServiceConfig;
import com.yonge.cooleshow.biz.dal.dao.*;
import com.yonge.cooleshow.biz.dal.dto.search.QueryMyFollowSearch;
import com.yonge.cooleshow.biz.dal.dto.search.StudentSearch;
import com.yonge.cooleshow.biz.dal.entity.*;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumMapper;
import com.yonge.cooleshow.biz.dal.mapper.TenantAlbumRefMapper;
import com.yonge.cooleshow.biz.dal.mapper.TenantGroupAlbumMapper;
import com.yonge.cooleshow.biz.dal.mapper.TenantGroupMapper;
import com.yonge.cooleshow.biz.dal.mapper.UserTenantBindRecordMapper;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.service.im.ImGroupCoreService;
import com.yonge.cooleshow.biz.dal.vo.MyFollow;
import com.yonge.cooleshow.biz.dal.vo.StudentHomeVo;
import com.yonge.cooleshow.biz.dal.vo.StudentVo;
import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
import com.yonge.cooleshow.biz.dal.wrapper.StudentWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
import com.yonge.cooleshow.common.constant.SysConfigConstant;
import com.yonge.cooleshow.common.entity.HttpResponseResult;
import com.yonge.cooleshow.common.enums.CacheNameEnum;
import com.yonge.cooleshow.common.enums.UserLockFlag;
import com.yonge.cooleshow.common.enums.YesOrNoEnum;
import com.yonge.toolset.base.util.StringUtil;
import com.yonge.toolset.mybatis.support.PageUtil;
import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
import com.yonge.toolset.utils.date.DateUtil;
import com.yonge.toolset.utils.easyexcel.ExcelDataReaderProperty;
import com.yonge.toolset.utils.string.ValueUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service
public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> implements StudentService {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StudentTotalService totalService;
    @Autowired
    private ImUserFriendService imUserFriendService;
    @Resource
    private UserBindingTeacherDao userBindingTeacherDao;
    @Resource
    private TeacherDao teacherDao;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SubjectDao subjectDao;
    @Autowired
    private TenantInfoService tenantInfoService;
    @Autowired
    private UserTenantAlbumRecordService userTenantAlbumRecordService;
    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private ImGroupService imGroupService;

    @Autowired
    private ImGroupMemberService imGroupMemberService;

    @Autowired
    private TenantAlbumRefMapper tenantAlbumRefMapper;

    @Autowired
    private TenantAlbumMapper tenantAlbumMapper;

    @Autowired
    private EmployeeDao employeeDao;

    @Autowired
    private SysUserFeignService sysUserFeignService;

    @Autowired
    private CustomerServiceConfig customerServiceConfig;


    @Autowired
    private SysMessageService sysMessageService;

    @Override
    public StudentDao getDao() {
        return baseMapper;
    }

    @Autowired
    private ImGroupCoreService imGroupCoreService;

    @Autowired
    private TenantGroupMapper tenantGroupMapper;

    @Autowired
    private UserTenantBindRecordMapper userTenantBindRecordMapper;

    @Autowired
    private TenantGroupAlbumMapper tenantGroupAlbumMapper;
    @Override
    public StudentVo detail(Long userId) {
        return baseMapper.detail(userId);
    }

    @Override
    public StudentVo detailByPhone(String phone) {
        return baseMapper.detailByPhone(phone);
    }

    @Override
    public IPage<StudentVo> selectPage(IPage<StudentVo> page, StudentSearch studentSearch) {
        return page.setRecords(baseMapper.selectPage(page, studentSearch));
    }

    @Override
    public StudentHomeVo queryUserInfo(SysUser user) {
        StudentVo detail = detail(user.getId());
        StudentHomeVo studentHomeVo = new StudentHomeVo();
        BeanUtils.copyProperties(detail, studentHomeVo);

        try {
            ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(user.getId().toString(),
                    ClientEnum.STUDENT.getCode(),
                    user.getUsername(),
                    user.getAvatar());
            studentHomeVo.setImToken(register.getImToken());
        } catch (Exception e) {
            throw new BizException("获取IMToken失败");
        }

        //身份证号、手机号脱敏
        studentHomeVo.setIdCardNo(ValueUtil.fuzzyIdCard(studentHomeVo.getIdCardNo()));
//        studentHomeVo.setPhone(ValueUtil.fuzzyMobile(studentHomeVo.getPhone()));
        String avatar = user.getAvatar();
        if (StringUtils.isNotBlank(detail.getAvatar())) {
            avatar = detail.getAvatar();
        }
        studentHomeVo.setHeardUrl(avatar);

        int num = DateUtil.daysBetween(new Date(), detail.getMembershipEndTime());
        studentHomeVo.setMembershipDays(num < 0 ? 0 : num);

        StudentTotal total = totalService.getTotalById(user.getId());
        if (null == total) {
            total = new StudentTotal();
        }

        studentHomeVo.setTenantAlbumStatus(0);
        studentHomeVo.setExerciseDays(null == total.getExerciseDays() ? 0 : total.getExerciseDays());
        studentHomeVo.setExerciseHours(null == total.getExerciseHours() ? 0 : total.getExerciseHours());
        studentHomeVo.setTestingNum(null == total.getTestingNum() ? 0 : total.getTestingNum());
        studentHomeVo.setFinshClassHours(null == total.getFinshHours() ? 0 : total.getFinshHours());
        studentHomeVo.setUnfinshClassHours(null == total.getUnfinshHours() ? 0 : total.getUnfinshHours());
        studentHomeVo.setStarTeacherNum(null == total.getStarTeacherNum() ? 0 : total.getStarTeacherNum());
        studentHomeVo.setMusicAlbumNum(null == total.getMusicAlbumNum() ? 0 : total.getMusicAlbumNum());
        studentHomeVo.setMusicSheetNum(null == total.getMusicSheetNum() ? 0 : total.getMusicSheetNum());

        // IM聊天用户ID
        studentHomeVo.setImUserId(imGroupService.getImUserId(String.valueOf(detail.getUserId()), ClientEnum.STUDENT.name()));

        // 判断是否是机构学生 机构学生 检测机构专辑购买记录
        TenantInfo tenantInfo = tenantInfoService.detail(detail.getTenantId());
        if (tenantInfo != null) {
            studentHomeVo.setTenantName(tenantInfo.getName());
            QueryWrapper<TenantGroupAlbum> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(TenantGroupAlbum::getTenantGroupId, Optional.ofNullable(detail.getTenantGroupId()).orElse(-1L));
            List<TenantGroupAlbum> tenantGroupAlbums = tenantGroupAlbumMapper.selectList(queryWrapper);
            List<Long> albIds = tenantGroupAlbums.stream().map(TenantGroupAlbum::getTenantAlbumId).distinct().collect(Collectors.toList());
            if (!tenantGroupAlbums.isEmpty()) {
                studentHomeVo.setTenantAlbumStatus(1);
            }
            List<UserTenantAlbumRecord> record = userTenantAlbumRecordService.lambdaQuery()
                    .eq(UserTenantAlbumRecord::getUserId, detail.getUserId())
                    .eq(UserTenantAlbumRecord::getClientType, ClientEnum.STUDENT)
                    .gt(UserTenantAlbumRecord::getEndTime,new Date())
                    .orderByAsc(UserTenantAlbumRecord::getEndTime)
                    .list();
            if (record.isEmpty()) {
                studentHomeVo.setTenantAlbumFlag(YesOrNoEnum.NO);
            } else {
                long count = record.stream().filter(next -> albIds.contains(next.getTenantAlbumId())).count();
                if (count > 0) {
                    studentHomeVo.setTenantAlbumStatus(2);
                }
                studentHomeVo.setTenantAlbumFlag(YesOrNoEnum.YES);
                studentHomeVo.setTenantAlbumStartTime(record.get(0).getStartTime());
                studentHomeVo.setTenantAlbumEndTime(record.get(0).getEndTime());
            }

        }
        if (detail.getTenantGroupId() != null) {
            TenantGroup tenantGroup = tenantGroupMapper.selectById(detail.getTenantGroupId());
            if (tenantGroup != null) {
                studentHomeVo.setTenantGroupName(tenantGroup.getName());
            }
        }

        return studentHomeVo;
    }

    @Override
    public Integer setSubject(Long id, String subjectIds) {
        return baseMapper.setSubject(id, subjectIds);
    }

    @Override
    public List<Subject> querySubject(Long userId) {
        List<Subject> subjects = baseMapper.querySubject(userId);
        return subjects;
    }

    @Override
    public List<Subject> querySubjectItem(Long userId, String type) {
        //先查询自己的声部
        List<Subject> subjectList = querySubject(userId);

        List<Subject> cacheByType = getCacheByType(userId, type);
        if (CollectionUtils.isNotEmpty(cacheByType)) {
            subjectList.addAll(cacheByType);
        }
        return subjectList;
    }

    private List<Subject> getCacheByType(Long userId, String type) {
        RMap<String, List<Subject>> map = redissonClient.getMap(CacheNameEnum.STUDENT_SUBJECT_ITEM.getRedisKey(userId));
        //设置过期时间
        map.expire(CacheNameEnum.STUDENT_SUBJECT_ITEM.getDuration().toMinutes(),
                TimeUnit.MINUTES);

        if (StringUtil.isEmpty(type)) {
            List<Subject> subjectList = map.get("ALL");
            if (CollectionUtils.isEmpty(subjectList)) {
                subjectList = baseMapper.querySubjectItem(userId, null);
                map.put("ALL", subjectList);
            }
            return subjectList;
        } else {
            List<Subject> subjectList = map.get(type);
            if (CollectionUtils.isEmpty(subjectList)) {
                subjectList = baseMapper.querySubjectItem(userId, type);
                map.put(type, subjectList);
            }
            return subjectList;
        }
    }

    @Override
    public IPage<MyFollow> queryMyFollow(IPage<MyFollow> page, QueryMyFollowSearch query) {
        List<MyFollow> teacherVos = baseMapper.queryMyFollow(page, query);
        return page.setRecords(teacherVos);
    }

    @Override
    public HttpResponseResult<Map<String, TeacherVo>> bindTeacher(String phone, Long userId, Boolean isUpdate) {
        Map<String, TeacherVo> resMap = new HashMap<>();
        TeacherVo old = userBindingTeacherDao.getBindTeacherByPhone(phone);
        if (null != old) {
            //身份证号、手机号脱敏
            old.setIdCardNo(ValueUtil.fuzzyIdCard(old.getIdCardNo()));
            old.setPhone(ValueUtil.fuzzyMobile(old.getPhone()));
        }
        resMap.put("old", old);

        StudentVo studentVo = detailByPhone(phone);

        if (null == studentVo) {
            return HttpResponseResult.failed("未找到用户信息");
        }
        if (studentVo.getTenantId() != null && studentVo.getTenantId() > 0) {
            throw  new BizException("不可邀请机构学生");
        }
        TeacherVo detail = teacherDao.detail(userId);
        if (null != detail) {
            //身份证号、手机号脱敏
            detail.setIdCardNo(ValueUtil.fuzzyIdCard(detail.getIdCardNo()));
            detail.setPhone(ValueUtil.fuzzyMobile(detail.getPhone()));
        }
        if (null == detail) {
            return HttpResponseResult.failed("未找老师信息");
        }
        if (UserLockFlag.LOCKED.equals(detail.getLockFlag())) {
            return HttpResponseResult.failed("老师被锁定");
        }
        if (null == old || (null != isUpdate && isUpdate)) {
            //更新
            //删除用户绑定
            userBindingTeacherDao.delete(Wrappers.<UserBindingTeacher>lambdaQuery()
                    .eq(UserBindingTeacher::getStudentId, studentVo.getUserId()));

            UserBindingTeacher userBindingTeacher = new UserBindingTeacher();
            userBindingTeacher.setTeacherId(userId);
            userBindingTeacher.setStudentId(studentVo.getUserId());
            userBindingTeacherDao.insert(userBindingTeacher);

            //学生老师增加好友关系
            Set<Long> studentIds = new HashSet<>();
            studentIds.add(studentVo.getUserId());
            imUserFriendService.saveUserFriend(userId, studentIds);
        }
        resMap.put("now", detail);
        return HttpResponseResult.succeed(resMap);
    }

    /**
     * 学生帐号关联
     *
     * @param info StudentWrapper.UnionStudent
     * @return StudentWrapper.UnionStudentResp
     */
    @Transactional
    @Override
    public StudentWrapper.UnionStudentResp unionStudent(StudentWrapper.UnionStudent info) {

        // 帐号关联响应数据
        StudentWrapper.UnionStudentResp studentResp = StudentWrapper.UnionStudentResp
                .builder()
                .unionId(info.getUnionId())
                .mobile(info.getMobile())
                .updateFlag(false)
                .build();

        // 手机号查询用户信息
        StudentVo studentVo = detailByPhone(info.getMobile());


        com.yonge.cooleshow.biz.dal.entity.SysUser sysUser =
                sysUserMapper.selectOne(Wrappers.<com.yonge.cooleshow.biz.dal.entity.SysUser>lambdaQuery()
                        .eq(com.yonge.cooleshow.biz.dal.entity.SysUser::getPhone, info.getMobile()));

        studentResp.setSysUser(sysUser);
        if (Objects.isNull(studentVo)) {

            if (Objects.isNull(sysUser)) {

                // 自动注册创建用户
                com.yonge.cooleshow.biz.dal.entity.SysUser user = new com.yonge.cooleshow.biz.dal.entity.SysUser();
                user.setUsername(info.getUsername());
                user.setAvatar("https://oss.dayaedu.com/daya/202203/T1WgJaE.png");
                user.setPhone(info.getMobile());
                user.setLockFlag(0);
                user.setGender(info.getGender());
                user.setUserType(ClientEnum.STUDENT.getCode());
                sysUserMapper.insert(user);

                studentResp.username(user.getUsername()).userId(user.getId()).setSysUser(user);

                // 更新标识
                studentResp.setUpdateFlag(true);
            } else {

                com.yonge.cooleshow.biz.dal.entity.SysUser updateEntity =
                        new com.yonge.cooleshow.biz.dal.entity.SysUser();
                updateEntity.setId(sysUser.getId());
                updateEntity.setUpdateTime(LocalDateTime.now());
                //updateEntity.setDelFlag(Optional.ofNullable(info.getLockFlag()).orElse(0)); // 激活帐号
                if (StringUtils.isBlank(sysUser.getUsername())) {
                    updateEntity.setUsername(info.getUsername());
                }
                // 激活帐号
                sysUserMapper.updateById(updateEntity);

                studentResp.username(sysUser.getUsername()).userId(sysUser.getId());

                Student student = lambdaQuery().eq(Student::getUserId, sysUser.getId()).one();

                if (Objects.nonNull(student)) {
                    // 重置学生隐藏状态
                    info.setLockFlag(0);
                }
                // 更新学生关联帐号
                updateStudentUnionStatus(info, studentResp, student);

            }

            String subjectId = null;
            // 学生声部
            if (!StringUtil.isEmpty(info.getSubjectName())) {
                Subject subject = subjectDao.getSubjectMatchByName(info.getSubjectName());
                if (Objects.nonNull(subject)) {
                    subjectId = String.valueOf(subject.getId());
                }
            }

            // 添加学生帐号
            Student student = new Student();
            student.setUserId(studentResp.getUserId());
            student.setSubjectId(subjectId);
            student.setUnionId(info.getUnionId());
            student.setLockFlag(UserLockFlag.NORMAL);
            student.setHideFlag(Optional.ofNullable(info.getLockFlag()).orElse(0));
            save(student);

        } else {

            studentResp.username(studentVo.getUsername()).userId(studentVo.getUserId());

            Student student = lambdaQuery().eq(Student::getUserId, studentVo.getUserId()).one();

            // 重置学生隐藏状态
            info.setLockFlag(0);
            // 更新学生关联帐号
            updateStudentUnionStatus(info, studentResp, student);
        }

        return studentResp;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean save(StudentWrapper.Student studentInfo) {
        if (studentInfo.getTenantGroupId() == null) {
            studentInfo.setTenantGroupId(-1L);
        }

        if (studentInfo.getId() == null) {
            return createStudent(studentInfo);
        } else {
            return updateStudent(studentInfo);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importStudentExcel(List<ExcelDataReaderProperty<StudentWrapper.StudentImport>> dataList,
                                   Long tenantId, Long userId) {
        if (dataList.isEmpty()) {
            throw new BizException("导入数据不能为空");
        }
        dataList.sort(Comparator.comparingInt(ExcelDataReaderProperty::getRowIndex));

        QueryWrapper<TenantGroup> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TenantGroup::getTenantId, tenantId);
        List<TenantGroup> groups = tenantGroupMapper.selectList(queryWrapper);
        Map<String, Long> groupNameIdMap = groups.stream().collect(Collectors.toMap(TenantGroup::getName, TenantGroup::getId));

        Map<String, Long> subjectIdNamemap = subjectDao.findAll(new HashMap<>())
                .stream().collect(Collectors.toMap(next -> next.getName(), next -> next.getId()));
        Set<String> subjectNames = subjectIdNamemap.keySet();
        // 校验数据的完整性
        List<String> errMsg = new ArrayList<>();
        Map<String, Integer> phoneMap = new HashMap<>();
        int rowIndex = 0;
        for (ExcelDataReaderProperty<StudentWrapper.StudentImport> next : dataList) {
            rowIndex++;
            StudentWrapper.StudentImport student = next.getClazz();

            int msgRowNo = rowIndex + 1;
            student.checkValid().forEach(err -> errMsg.add(String.format("第%s行%s", msgRowNo, err)));

            if (StringUtils.isNotEmpty(student.getPhone())) {
                if (phoneMap.containsKey(student.getPhone())) {
                    errMsg.add(String.format("第%s行手机号重复", msgRowNo));
                } else {
                    phoneMap.put(student.getPhone(), msgRowNo);
                }
            }

            if (StringUtils.isNotEmpty(student.getSubjectName()) && !subjectNames.contains(student.getSubjectName())) {
                errMsg.add(String.format("第%s行声部不支持", msgRowNo));
            }

            if (StringUtils.isNotEmpty(student.getTenantGroupName()) &&
                    !groupNameIdMap.containsKey(student.getTenantGroupName())) {
                errMsg.add(String.format("第%s行小组不存在", msgRowNo));
            }

            if (errMsg.size() > 100) {
                break;
            }
        }

        if (!errMsg.isEmpty()) {
            throw new BizException(String.join(",", errMsg));
        }

        int row = 50;
        int page = 1;
        List<List<String>> phonePartition = Lists.partition(new ArrayList<>(phoneMap.keySet()), row);

        StudentSearch studentSearch = new StudentSearch();
        studentSearch.setRows(row);
        studentSearch.setPage(page);
        for (List<String> phones : phonePartition) {
            studentSearch.setPhoneList(phones);
            List<StudentVo> studentVos = baseMapper.selectPage(PageUtil.getPage(studentSearch), studentSearch);
            page++;

            if (!studentVos.isEmpty()) {
                List<String> existPhoneList = studentVos.stream().map(StudentVo::getPhone).collect(Collectors.toList());
                existPhoneList.forEach(phone -> {
                    if (phoneMap.containsKey(phone)) {
                        errMsg.add(String.format("第%s行手机号已经注册学生", phoneMap.get(phone)));
                    }
                });
            }
        }

        if (!errMsg.isEmpty()) {
            throw new BizException(String.join(",", errMsg));
        }

        for (ExcelDataReaderProperty<StudentWrapper.StudentImport> next : dataList) {
            StudentWrapper.StudentImport studentImport = next.getClazz();
            StudentWrapper.Student student = new StudentWrapper.Student();
            student.setTenantId(tenantId);
            student.setName(studentImport.getUserName());
            student.setGender("男".equals(studentImport.getGender()) ? 1 : 0);
            student.setSubjectId(subjectIdNamemap.get(studentImport.getSubjectName()).toString());
            student.setPhone(studentImport.getPhone());
            student.setTenantGroupId(groupNameIdMap.getOrDefault(studentImport.getTenantGroupName(), -1L));

            LocalDate birthday = LocalDate.parse(studentImport.getBirthday(), DateTimeFormatter.ISO_LOCAL_DATE);
            student.setBirthdate(birthday);
            save(student);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTenant(Student student, Long toTenantId) {
        Long tenantId = student.getTenantId();
        if (tenantId.equals(toTenantId)) {
            return;
        }
        if (!tenantId.equals(-1L)) {
            // 退群
            List<ImGroupMember> groupMembers = imGroupMemberService.lambdaQuery()
                    .eq(ImGroupMember::getRoleType, ImGroupMemberRoleType.STUDENT)
                    .eq(ImGroupMember::getIsAdmin, false)
                    .eq(ImGroupMember::getUserId, student.getUserId())
                    .list();
            List<String> groupIdList = groupMembers.stream().map(ImGroupMember::getGroupId)
                    .distinct().collect(Collectors.toList());
            if (!groupIdList.isEmpty()) {
                List<String> orgGroupIdList = imGroupService.listByIds(groupIdList).stream()
                        .filter(next -> ImGroupType.ORG.equals(next.getType())).map(ImGroup::getId)
                        .collect(Collectors.toList());
                for (String groupId : orgGroupIdList) {
                    try {
                        imGroupService.quit(groupId, student.getUserId(), ClientEnum.STUDENT,true);
                    } catch (Exception e) {
                        log.error("学生注册，切换机构退群失败：{}", e.getMessage());
                        throw new BizException("退群失败");
                    }
                }
            }
//             删除好友
            imUserFriendService.delTeacherFriendByTenantId(student.getTenantId(), student.getUserId(),
                    ClientEnum.STUDENT.getCode());
            addBindUnBindRecord(student.getUserId(),student.getTenantId(),false);
        }
        if (toTenantId != null && toTenantId != -1L) {
            // 加好友
            QueryWrapper<Teacher> query = new QueryWrapper<>();
            query.lambda().eq(Teacher::getTenantId, toTenantId);
            List<Teacher> teachers = teacherDao.selectList(query);
            for (Teacher teacher : teachers) {
                HashSet<Long> studentIds = new HashSet<>();
                studentIds.add(student.getUserId());
                imUserFriendService.saveUserFriend(teacher.getUserId(), studentIds);
            }
            addBindUnBindRecord(student.getUserId(),student.getTenantId(),true);
        }
        this.lambdaUpdate().set(Student::getTenantId, toTenantId)
                .set(Student::getTenantGroupId, -1L)
                .eq(Student::getUserId, student.getUserId())
                .update();

        sendStudentTenantChange(student,toTenantId);
    }

    @Override
    public void sendStudentTenantChange(Student student, Long toTenantId) {
        if (student.getTenantId() == null || student.getTenantId() == -1) {
            return;
        }
        SysUser sysUser = sysUserFeignService.queryUserById(student.getUserId());
        if (sysUser == null) {
            return;
        }
        TenantInfo tenantInfo = tenantInfoService.getById(student.getTenantId());
        if (tenantInfo == null) {
            return;
        }
        TenantInfo newTenantInfo = tenantInfoService.getById(toTenantId);
        if (newTenantInfo == null) {
            return;
        }
        Map<Long, String> receivers = new HashMap<>();
        receivers.put(student.getUserId(), sysUser.getPhone());
        try {
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TENANT_STUDENT_CHANGE,
                    receivers, null, 0, null, ClientEnum.TENANT_STUDENT.getCode(),tenantInfo.getName(),newTenantInfo.getName());
        } catch (Exception e) {
            log.error("学生机构变更极光消息推送异常,", e);
        }
    }

    @Override
    public Map<Long, Student> getMapByIds(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return new HashMap<>();
        }
        List<Student> students = baseMapper.selectBatchIds(userIds);
        return students.stream().collect(Collectors.toMap(Student::getUserId, Function.identity()));

    }

    private Boolean updateStudent(StudentWrapper.Student studentInfo) {
        StudentVo student = detail(studentInfo.getId());
        if (student == null) {
            throw new BizException("学生信息不存在");
        }
        // 是否有修改机构
        Long newTenantId = studentInfo.getTenantId();
        if(newTenantId != null && !newTenantId.equals(student.getTenantId()) && student.getTenantId() != -1L){
            // 退群
            List<ImGroupMember> groupMembers = imGroupMemberService.lambdaQuery()
                    .eq(ImGroupMember::getRoleType, ImGroupMemberRoleType.STUDENT)
                    .eq(ImGroupMember::getIsAdmin, false)
                    .eq(ImGroupMember::getUserId, student.getUserId())
                    .list();
            List<String> groupIdList = groupMembers.stream().map(ImGroupMember::getGroupId)
                    .distinct().collect(Collectors.toList());
            if (!groupIdList.isEmpty()) {
                List<String> orgGroupIdList = imGroupService.listByIds(groupIdList).stream()
                        .filter(next -> ImGroupType.ORG.equals(next.getType())).map(ImGroup::getId)
                        .collect(Collectors.toList());
                for (String groupId : orgGroupIdList) {
                    try {
                        imGroupService.quit(groupId, student.getUserId(), ClientEnum.STUDENT,true);
                    } catch (Exception e) {
                        log.error("学生注册，切换机构退群失败：{}", e.getMessage());
                        throw new BizException("退群失败");
                    }
                }
            }
//             删除好友
            imUserFriendService.delTeacherFriendByTenantId(student.getTenantId(), student.getUserId(),
                    ClientEnum.STUDENT.getCode());

            addBindUnBindRecord(student.getUserId(),student.getTenantId(),false);

            // 加好友
            QueryWrapper<Teacher> query = new QueryWrapper<>();
            query.lambda().eq(Teacher::getTenantId, newTenantId);
            List<Teacher> teachers = teacherDao.selectList(query);
            for (Teacher teacher : teachers) {
                HashSet<Long> studentIds = new HashSet<>();
                studentIds.add(student.getUserId());
                imUserFriendService.saveUserFriend(teacher.getUserId(), studentIds);
            }

            addBindUnBindRecord(student.getUserId(),studentInfo.getTenantId(),true);
            sendStudentTenantChange(student,newTenantId);
        }


        // 手机号码修改
        boolean updatePhone = false;
        if (!student.getPhone().equals(studentInfo.getPhone())) {
            updatePhone = true;
            SysUser oldSysUser = sysUserMapper.getByUserId(studentInfo.getId());
            com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = updateAccount(studentInfo, oldSysUser);
            this.lambdaUpdate().set(Student::getSubjectId, studentInfo.getSubjectId())
                    .set(Student::getTenantId, studentInfo.getTenantId())
//                    .set(Student::getUserId, sysUser.getId())
                    .set(Student::getUpdateTime, new Date())
                    .set(Student::getTenantGroupId, studentInfo.getTenantGroupId())
                    .set(StringUtils.isNotEmpty(studentInfo.getAvatar()), Student::getAvatar, studentInfo.getAvatar())
                    .eq(Student::getUserId, studentInfo.getId())
                    .update();

            userTenantBindRecordMapper.update(null, Wrappers.<UserTenantBindRecord>lambdaUpdate()
                    .eq(UserTenantBindRecord::getUserId, studentInfo.getId())
                    .eq(UserTenantBindRecord::getUserType, "STUDENT")
                    .set(UserTenantBindRecord::getUserId, sysUser.getId()));
        } else {
            Student newStudent = new Student();
            newStudent.setUserId(studentInfo.getId());
            newStudent.setSubjectId(studentInfo.getSubjectId());
            newStudent.setUpdateTime(new Date());
            newStudent.setTenantId(studentInfo.getTenantId());
            newStudent.setAvatar(studentInfo.getAvatar());
            newStudent.setTenantGroupId(studentInfo.getTenantGroupId());
            this.getBaseMapper().updateById(newStudent);
        }
        com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = sysUserMapper.selectById(studentInfo.getId());
        sysUser.setGender(studentInfo.getGender());
        sysUser.setAvatar(studentInfo.getAvatar());
        sysUser.setUsername(studentInfo.getName());
        sysUser.setBirthdate(studentInfo.getBirthdate());
        sysUserMapper.updateById(sysUser);

        try {
            if (StringUtils.isNotEmpty(sysUser.getAvatar())) {
                // 注册IM
                imGroupCoreService.register(String.valueOf(student.getUserId()), ClientEnum.STUDENT.getCode(),
                        sysUser.getUsername(), sysUser.getAvatar());
            }
        } catch (Exception e) {
            log.warn("学生注册IM失败：" + e.getMessage());
        }
        // 手机号修改，退出登录
        if (updatePhone) {
            sysUserFeignService.exitByPhone(ClientEnum.STUDENT.getCode().toLowerCase(), student.getPhone());
        }

        return true;
    }

    private void quitFromTenantGroupIM(Long tenantGroupId, Long studentId) {
        TenantGroup tenantGroup = tenantGroupMapper.selectById(tenantGroupId);
        if (tenantGroup == null || StringUtils.isEmpty(tenantGroup.getImGroupId())) {
            return;
        }
        String imGroupId = tenantGroup.getImGroupId();
        ImGroup imGroup = imGroupService.getById(imGroupId);
        if (imGroup == null) {
            return;
        }
        Integer count = imGroupMemberService.lambdaQuery()
                .eq(ImGroupMember::getId, imGroupId)
                .eq(ImGroupMember::getUserId, studentId)
                .eq(ImGroupMember::getRoleType, ImGroupMemberRoleType.STUDENT)
                .count();
        if (count == 0) {
            return;
        }
        try {
            imGroupService.quit(imGroupId, studentId, ClientEnum.STUDENT, true);
        } catch (Exception e) {
            log.error("退出机构小组群失败", e);
            throw new BizException("退出机构小组群失败");
        }
    }

    /**
     * 添加到机构小组IM群
     * @param tenantGroupId 机构小组ID
     * @param studentId 学生ID
     */
    private void add2TenantGroupIM(Long tenantGroupId, Long studentId) {
        TenantGroup tenantGroup = tenantGroupMapper.selectById(tenantGroupId);
        String imGroupId = tenantGroup.getImGroupId();
        if (imGroupId == null) {
            // 建群
            try {
                ImGroupWrapper.ImGroup imGroup = new ImGroupWrapper.ImGroup();
                imGroup.setId(IdWorker.getId());
                imGroup.setCreateBy(tenantGroup.getAdminId());
                imGroup.setType(ImGroupType.ORG.getCode());
                imGroup.setName(tenantGroup.getName());
                imGroupId = imGroupService.create(imGroup);
                tenantGroup.setImGroupId(imGroupId);
                tenantGroupMapper.updateById(tenantGroup);
            } catch (Exception e) {
                log.error("创建机构小组群失败", e);
                throw new BizException("创建机构小组群失败");
            }
        }
        if ("-1".equals(imGroupId)) {
            // 群已经解散，改为-1标志
            log.info("群已经解散，不再添加机构小组群:" + imGroupId);
            return;
        }
        try {
            ImGroup imGroup = imGroupService.getById(imGroupId);
            if (imGroup != null) {
                Set<Long> userIds = new HashSet<>();
                userIds.add(studentId);
                imGroupService.addGroupMember(imGroupId, userIds);
            } else {
                tenantGroup.setImGroupId("-1");
                tenantGroupMapper.updateById(tenantGroup);
            }
        } catch (Exception e) {
            log.error("加入机构小组群失败", e);
            throw new BizException("加入机构小组群失败");
        }
    }

    private Boolean createStudent(StudentWrapper.Student studentInfo) {
        String avatar = studentInfo.getAvatar();
        if (StringUtils.isEmpty(avatar)) {
            avatar = sysConfigService.findConfigValue(SysConfigConstant.STUDENT_AVATAR);
            studentInfo.setAvatar(avatar);
        }
        com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = getOrCreateAccount(studentInfo, null);
        Student student = new Student();
        student.setUserId(sysUser.getId());
        student.setTenantId(studentInfo.getTenantId());
        student.setSubjectId(studentInfo.getSubjectId());
        student.setAvatar(studentInfo.getAvatar());
        student.setCreateTime(new Date());
        student.setLockFlag(UserLockFlag.NORMAL);
        student.setTenantGroupId(studentInfo.getTenantGroupId());

        save(student);
        try {
            // 注册IM
            imGroupCoreService.register(String.valueOf(student.getUserId()),ClientEnum.STUDENT.getCode(), sysUser.getUsername(), avatar);
        } catch (Exception e) {
            log.warn("学生注册IM失败："+e.getMessage());
        }

        // 加好友
        if (student.getTenantId() != null && student.getTenantId() != -1L) {
            // 自动与机构老师成为好友
            QueryWrapper<Teacher> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(Teacher::getTenantId, student.getTenantId());
            List<Teacher> teacherList = teacherDao.selectList(queryWrapper);
            teacherList.forEach(next -> imUserFriendService.saveUserFriend(next.getUserId(),
                    new HashSet<>(ImmutableList.of(sysUser.getId()))));

            addBindUnBindRecord(student.getUserId(), student.getTenantId(), true);
        }

        //  与随机一个客服建立好友
        String customerService = customerServiceConfig.getCustomerService();
        if (StringUtils.isNotBlank(customerService)) {
            List<String> phones = Arrays.stream(customerService.split(",")).collect(Collectors.toList());
            Random rand = new Random();
            String mobile = phones.get(rand.nextInt(phones.size()));
            SysUser friend = sysUserMapper.findUserByPhone(mobile);
            if (friend != null) {
                imUserFriendService.registerUserBindCustomerService(student.getUserId(),
                        Collections.singletonList(friend.getId()), ClientEnum.STUDENT);
            }
        }

        // 加入机构小组群
//        if (studentInfo.getTenantGroupId() != -1L) {
//            add2TenantGroupIM(student.getTenantGroupId(), student.getUserId());
//        }

        return true;
    }

    private com.yonge.cooleshow.biz.dal.entity.SysUser getOrCreateAccount(StudentWrapper.Student studentInfo,
                                                                          String password) {
        QueryWrapper<com.yonge.cooleshow.biz.dal.entity.SysUser> sysUserQueryWrapper = new QueryWrapper<>();
        sysUserQueryWrapper.lambda().eq(com.yonge.cooleshow.biz.dal.entity.SysUser::getPhone, studentInfo.getPhone());
        List<com.yonge.cooleshow.biz.dal.entity.SysUser> sysUsers = sysUserMapper.selectList(sysUserQueryWrapper);
        com.yonge.cooleshow.biz.dal.entity.SysUser sysUser;
        if (!sysUsers.isEmpty()) {
            sysUser = sysUsers.get(0);
            if (sysUser.getUserType().contains(ClientEnum.STUDENT.getCode())) {
                throw new BizException("手机号已经注册学生账号");
            }
            sysUser.setAvatar(studentInfo.getAvatar());
            sysUser.setGender(studentInfo.getGender());
            sysUser.setUsername(studentInfo.getName());
            sysUser.setBirthdate(studentInfo.getBirthdate());
            String userType = sysUser.getUserType();
            String studentUserType = "STUDENT";
            if (StringUtils.isEmpty(userType)) {
                sysUser.setUserType(studentUserType);
            } else if (!userType.contains(studentUserType)) {
                sysUser.setUserType(userType + "," + studentUserType);
            }
            sysUserMapper.updateById(sysUser);
        } else {
            sysUser = JSON.parseObject(JSON.toJSONString(studentInfo),
                    com.yonge.cooleshow.biz.dal.entity.SysUser.class);
            sysUser.setPhone(studentInfo.getPhone());
            sysUser.setAvatar(studentInfo.getAvatar());
            if (StringUtils.isEmpty(studentInfo.getAvatar())) {
                //设置默认头像
                String avatar = sysConfigService.findConfigValue(SysConfigConstant.STUDENT_AVATAR);
                sysUser.setAvatar(avatar);
            }
            sysUser.setGender(studentInfo.getGender());
            sysUser.setUserType("STUDENT");
            sysUser.setBirthdate(studentInfo.getBirthdate());
            sysUser.setUsername(studentInfo.getName());
            sysUser.setPassword(password);

            if (StringUtils.isEmpty(password)) {
                String newPassword = MessageFormat.format("klx{0}", studentInfo.getPhone().substring(7));
                sysUser.setPassword(new BCryptPasswordEncoder().encode(newPassword));
            }

            sysUserMapper.insert(sysUser);
        }
        return sysUser;
    }

    private com.yonge.cooleshow.biz.dal.entity.SysUser updateAccount(StudentWrapper.Student studentInfo,SysUser oldSysUser) {
        QueryWrapper<com.yonge.cooleshow.biz.dal.entity.SysUser> sysUserQueryWrapper = new QueryWrapper<>();
        sysUserQueryWrapper.lambda().eq(com.yonge.cooleshow.biz.dal.entity.SysUser::getPhone, studentInfo.getPhone());
        List<com.yonge.cooleshow.biz.dal.entity.SysUser> sysUsers = sysUserMapper.selectList(sysUserQueryWrapper);
        if (!sysUsers.isEmpty()) {
            throw new BizException("手机号已经注册账号");
        } else {
            com.yonge.cooleshow.biz.dal.entity.SysUser sysUser = JSON.parseObject(JSON.toJSONString(studentInfo),
                    com.yonge.cooleshow.biz.dal.entity.SysUser.class);
            sysUser.setId(oldSysUser.getId());
            sysUser.setPhone(studentInfo.getPhone());
            sysUser.setAvatar(studentInfo.getAvatar());
            if (StringUtils.isEmpty(studentInfo.getAvatar())) {
                //设置默认头像
                String avatar = sysConfigService.findConfigValue(SysConfigConstant.STUDENT_AVATAR);
                sysUser.setAvatar(avatar);
            }
            sysUser.setGender(studentInfo.getGender());
            sysUser.setBirthdate(studentInfo.getBirthdate());
            sysUser.setUsername(studentInfo.getName());
            String password = oldSysUser.getPassword();
            sysUser.setPassword(password);

            if (StringUtils.isEmpty(password)) {
                String newPassword = MessageFormat.format("klx{0}", studentInfo.getPhone().substring(7));
                sysUser.setPassword(new BCryptPasswordEncoder().encode(newPassword));
            }
            sysUserMapper.updateById(sysUser);
            return sysUser;
        }
    }

    /**
     * 更新学生关联帐号
     *
     * @param info        StudentWrapper.UnionStudent
     * @param studentResp StudentWrapper.UnionStudentResp
     * @param student     Student
     */
    private void updateStudentUnionStatus(StudentWrapper.UnionStudent info,
                                          StudentWrapper.UnionStudentResp studentResp, Student student) {

        if (Objects.nonNull(student)) {

            if (Optional.ofNullable(student.getUnionId()).orElse(0L) <= 0) {

                // 更新学生关联Id
                lambdaUpdate()
                        .eq(Student::getUserId, student.getUserId())
                        .set(Student::getUnionId, info.getUnionId())
                        .set(Student::getHideFlag, Optional.ofNullable(info.getLockFlag()).orElse(0))
                        .update();
            } else {
                studentResp.setUnionId(student.getUnionId());
            }

            // 更新标识
            studentResp.setUpdateFlag(true);
        }
    }

    private void addBindUnBindRecord(Long studentId, Long tenantId, Boolean bind) {
        UserTenantBindRecord bindRecord = new UserTenantBindRecord();
        bindRecord.setUserId(studentId);
        bindRecord.setUserType("STUDENT");
        bindRecord.setTenantId(tenantId);
        bindRecord.setBindStatus(bind);
        bindRecord.setBindTime(new Date());
        userTenantBindRecordMapper.add(bindRecord);
    }
}