package com.ym.mec.biz.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.*;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.*;
import com.ym.mec.biz.dal.page.GroupCourseScheduleQueryInfo;
import com.ym.mec.biz.dal.page.GroupQueryInfo;
import com.ym.mec.biz.service.*;
import com.ym.mec.common.constant.CommonConstants;
import com.ym.mec.common.controller.BaseController;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.entity.HttpResponseResult;
import com.ym.mec.common.entity.ImGroupMember;
import com.ym.mec.common.entity.ImGroupModel;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.im.ImFeignService;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CoursesGroupServiceImpl extends BaseServiceImpl<Long, CoursesGroup> implements CoursesGroupService {

    @Autowired
    private CoursesGroupDao coursesGroupDao;
    @Autowired
    private CourseScheduleService courseScheduleService;
    @Autowired
    private ClassGroupDao classGroupDao;
    @Autowired
    private ClassGroupTeacherMapperDao classGroupTeacherMapperDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private CourseScheduleTeacherSalaryDao courseScheduleTeacherSalaryDao;
    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;
    @Autowired
    private SubjectDao subjectDao;
    @Autowired
    private ClassGroupStudentMapperDao classGroupStudentMapperDao;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private StudentPaymentOrderService studentPaymentOrderService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private PayService payService;
    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;
    @Autowired
    private GroupClassService groupService;
    @Autowired
    private SysUserCashAccountDetailService sysUserCashAccountDetailService;
    @Autowired
    private ImGroupService imGroupService;
    @Autowired
    private ImGroupMemberService imGroupMemberService;
    @Autowired
    private ImUserFriendService imUserFriendService;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private SysTenantAccountService sysTenantAccountService;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private GroupDao groupDao;
    @Autowired
    private StudentPaymentRouteOrderService studentPaymentRouteOrderService;

    private static final Logger LOGGER = LoggerFactory
            .getLogger(PracticeGroupService.class);

    @Override
    public BaseDAO<Long, CoursesGroup> getDAO() {
        return coursesGroupDao;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult createCourseGroup(CourseGroupCreateDto courseGroupCreateInfo) {
        CoursesGroup courseGroup = courseGroupCreateInfo.getCoursesGroup();
        CourseGenerateDto courseCycleInfo = courseGroupCreateInfo.getCourseCycleInfo();
        if (Objects.isNull(courseGroup.getName()) || StringUtils.isBlank(courseGroup.getName())) {
            throw new BizException("请填写课程名称");
        }
        if (Objects.isNull(courseGroup.getSubjectId())) {
            throw new BizException("请选择科目");
        }
        if (Objects.isNull(courseGroup.getMaxStudentNum())) {
            throw new BizException("请填写班级人数");
        }
        if (Objects.isNull(courseCycleInfo.getSingleClassMinutes())) {
            throw new BizException("请填写课程时长");
        }
        if (Objects.isNull(courseCycleInfo.getCourseCreateStartTime())) {
            throw new BizException("请填写课程开始时间");
        }
        if (Objects.isNull(courseCycleInfo.getCourseCount())) {
            throw new BizException("请填写课程数量");
        }

        studentDao.lockUser(courseGroup.getTeacherId());

        //总课程时长
        Integer totalCourseTime = courseCycleInfo.getCourseCount() * courseCycleInfo.getSingleClassMinutes();

        try {
            sysTenantAccountService.update(courseGroup.getTeacherId(), -totalCourseTime, "", SysTenantAccountDetail.TransType.CONSUME, "", BigDecimal.ZERO, "课程组创建");
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return BaseController.failed("余额不足，请充值。");
        }

        Teacher teacher = teacherDao.get(courseGroup.getTeacherId());

        Date now = new Date();
        courseGroup.setSingleClassMinutes(courseCycleInfo.getSingleClassMinutes());
        courseGroup.setStatus(GroupStatusEnum.NORMAL);
        courseGroup.setOrganId(teacher.getTeacherOrganId());
        List<CourseSchedule> newCourses = courseScheduleService.createCourses(courseGroupCreateInfo.getCourseCycleInfo(), true);
        for (CourseSchedule newCourse : newCourses) {
            newCourse.setTeachMode(TeachModeEnum.ONLINE);
            newCourse.setGroupType(GroupType.COMM);
            newCourse.setType(CourseSchedule.CourseScheduleType.COMM);
            newCourse.setTeacherId(courseGroup.getTeacherId());
            newCourse.setActualTeacherId(courseGroup.getTeacherId());
            newCourse.setIsLock(0);
            newCourse.setStatus(CourseStatusEnum.NOT_START);
            newCourse.setName(courseGroup.getName());
            newCourse.setOrganId(courseGroup.getOrganId());
        }

        CourseSchedule firstCourseSchedule = newCourses.stream().min(Comparator.comparing(CourseSchedule::getStartClassTime)).get();
        if (firstCourseSchedule.getStartClassTime().before(now)) {
            throw new BizException("开课时间不可小于当前时间");
        }
        CourseSchedule latestCourseSchedule = newCourses.stream().max(Comparator.comparing(CourseSchedule::getEndClassTime)).get();
        courseGroup.setCoursesStartDate(firstCourseSchedule.getStartClassTime());
        courseGroup.setCoursesEndDate(latestCourseSchedule.getEndClassTime());

        JSONArray courseTimesArray = new JSONArray();
        for (CourseTimeDto courseTime : courseCycleInfo.getCourseTimes()) {
            JSONObject courseTimeObject = new JSONObject();
            courseTimeObject.put(String.valueOf(courseTime.getDayOfWeek()), courseTime.getStartClassTime());
            courseTimesArray.add(courseTimeObject);
        }

        coursesGroupDao.insert(courseGroup);
        //创建班级信息
        ClassGroup classGroup = new ClassGroup();
        classGroup.setSubjectIdList(courseGroup.getSubjectId().toString());
        classGroup.setExpectStudentNum(courseGroup.getMaxStudentNum());
        classGroup.setName(courseGroup.getName());
        classGroup.setTotalClassTimes(courseCycleInfo.getCourseCount());
        classGroup.setType(ClassGroupTypeEnum.COMM);
        classGroup.setDelFlag(0);
        classGroup.setGroupType(GroupType.COMM);
        classGroup.setMusicGroupId(courseGroup.getId().toString());
        classGroup.setCreateTime(now);
        classGroup.setUpdateTime(now);
        classGroupDao.insert(classGroup);

        //创建班级老师关联记录
        ClassGroupTeacherMapper classGroupTeacherMapper = new ClassGroupTeacherMapper();
        classGroupTeacherMapper.setMusicGroupId(courseGroup.getId().toString());
        classGroupTeacherMapper.setClassGroupId(classGroup.getId());
        classGroupTeacherMapper.setTeacherRole(TeachTypeEnum.BISHOP);
        classGroupTeacherMapper.setUserId(courseGroup.getTeacherId());
        classGroupTeacherMapper.setGroupType(GroupType.COMM);
        classGroupTeacherMapper.setCreateTime(now);
        classGroupTeacherMapper.setUpdateTime(now);
        classGroupTeacherMapperDao.insert(classGroupTeacherMapper);

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = new ArrayList<>();
        List<TeacherAttendance> teacherAttendances = new ArrayList<>();

        for (CourseSchedule courseSchedule : newCourses) {
            //课表
            courseSchedule.setMusicGroupId(courseGroup.getId().toString());
            courseSchedule.setClassGroupId(classGroup.getId());
            courseSchedule.setCreateTime(now);
            courseSchedule.setUpdateTime(now);
        }

        try {
            courseScheduleService.checkNewCourseSchedules(newCourses, false,false);
        } catch (Exception e) {
            LOGGER.error("检查课程冲突出现异常",e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            String errMessage;
            String courseName = e.getMessage().substring(e.getMessage().indexOf(")-") + 2);
            courseName = courseName.substring(0, courseName.indexOf("("));
            String courseTime = e.getMessage().substring(e.getMessage().indexOf("在") + 1);
            courseTime = courseTime.substring(0, courseTime.indexOf("至"));
            errMessage = "现在课程：";
            errMessage += courseName;
            errMessage += "<br/>冲突时间：";
            errMessage += courseTime;
            return BaseController.failed(HttpStatus.FOUND, errMessage);
        }
        courseScheduleDao.batchAddCourseSchedules(newCourses);

        for (CourseSchedule courseSchedule : newCourses) {
            //课程与老师薪水表
            CourseScheduleTeacherSalary courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
            courseScheduleTeacherSalary.setCourseScheduleId(courseSchedule.getId());
            courseScheduleTeacherSalary.setGroupType(GroupType.COMM);
            courseScheduleTeacherSalary.setMusicGroupId(courseGroup.getId().toString());
            courseScheduleTeacherSalary.setTeacherRole(classGroupTeacherMapper.getTeacherRole());
            courseScheduleTeacherSalary.setUserId(courseGroup.getTeacherId());
            courseScheduleTeacherSalary.setExpectSalary(BigDecimal.ZERO);
            courseScheduleTeacherSalary.setCreateTime(now);
            courseScheduleTeacherSalary.setUpdateTime(now);
            courseScheduleTeacherSalary.setClassGroupId(classGroup.getId());
            courseScheduleTeacherSalaries.add(courseScheduleTeacherSalary);

            //教师签到记录
            TeacherAttendance teacherAttendance = new TeacherAttendance();
            teacherAttendance.setMusicGroupId(courseGroup.getId().toString());
            teacherAttendance.setTeacherId(courseGroup.getTeacherId());
            teacherAttendance.setClassGroupId(classGroup.getId());
            teacherAttendance.setGroupType(GroupType.COMM);
            teacherAttendance.setCourseScheduleId(courseSchedule.getId());
            teacherAttendance.setCreateTime(now);
            teacherAttendances.add(teacherAttendance);
        }
        courseScheduleTeacherSalaryDao.batchInsert(courseScheduleTeacherSalaries);
        teacherAttendanceDao.batchInsert(teacherAttendances);
        //创建群聊
        imGroupService.create(classGroup.getId().longValue(), null, classGroup.getName(), courseGroup.getName(), courseGroup.getName(), courseGroup.getName(), null, "COMM");
        Map<Integer,String> userRoleMap = new HashMap(1);
        userRoleMap.put(courseGroup.getTeacherId(),"指导老师");
        imGroupMemberService.join(classGroup.getId().longValue(), userRoleMap);
        return BaseController.succeed();
    }

    @Override
    public PageInfo findTeacherCourseGroups(Integer teacherId, GroupCourseScheduleQueryInfo queryInfo) {
        if (Objects.isNull(teacherId)) {
            throw new BizException("请指定老师");
        }

        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);
        params.put("teacherId", teacherId);

        List<CourseGroupTeacherCardDto> groupCards = new ArrayList<>();
        int count = coursesGroupDao.countTeacherGroups(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            List<CoursesGroup> teacherCourseGroups = coursesGroupDao.findTeacherCourseGroups(params);

            List<String> groupIds = teacherCourseGroups.stream().map(group -> String.valueOf(group.getId())).collect(Collectors.toList());

            List<Integer> subjectIds = teacherCourseGroups.stream().map(CoursesGroup::getSubjectId).collect(Collectors.toList());
            List<Subject> subjects = subjectDao.findBySubjectIds(subjectIds);
            Map<Integer, Subject> idSubjectMap = subjects.stream().collect(Collectors.toMap(Subject::getId, subject -> subject));

            List<ClassGroupStudentMapper> students = classGroupStudentMapperDao.findByGroups(groupIds, GroupType.COMM);
            Map<String, List<ClassGroupStudentMapper>> groupStudentsMap = students.stream().collect(Collectors.groupingBy(ClassGroupStudentMapper::getMusicGroupId));

            List<GroupCourseTimesDto> groupsCourseTimesInfo = courseScheduleDao.findGroupsCourseTimesInfo(groupIds, GroupType.COMM);
            Map<String, GroupCourseTimesDto> groupCourseTimesInfoMap = groupsCourseTimesInfo.stream().collect(Collectors.toMap(GroupCourseTimesDto::getGroupId, e -> e));

            List<GroupValidDateDto> groupsValidate = courseScheduleDao.findGroupsValidate(groupIds, GroupType.COMM);
            Map<String, GroupValidDateDto> groupValidateMap = groupsValidate.stream().collect(Collectors.toMap(GroupValidDateDto::getGroupId, e -> e));

            groupCards = new ArrayList<>();
            for (CoursesGroup teacherCourseGroup : teacherCourseGroups) {
                CourseGroupTeacherCardDto groupCard = new CourseGroupTeacherCardDto();
                groupCard.setId(teacherCourseGroup.getId());
                groupCard.setGroupType(GroupType.COMM);
                groupCard.setName(teacherCourseGroup.getName());
                groupCard.setSingleClassMinutes(teacherCourseGroup.getSingleClassMinutes());
                groupCard.setSubjectId(teacherCourseGroup.getSubjectId());
                groupCard.setGroupStatus(teacherCourseGroup.getStatus());
                groupCard.setSubjectName(idSubjectMap.get(teacherCourseGroup.getSubjectId()).getName());
                GroupCourseTimesDto groupCourseTimesInfo = groupCourseTimesInfoMap.get(String.valueOf(teacherCourseGroup.getId()));
                GroupValidDateDto groupValidDate = groupValidateMap.get(String.valueOf(teacherCourseGroup.getId()));
                if (Objects.nonNull(groupValidDate)) {
                    groupCard.setCoursesStartDate(groupValidDate.getStartDate());
                    groupCard.setCoursesEndDate(groupValidDate.getEndDate());
                }
                groupCard.setClassGroupId(groupCourseTimesInfo.getClassGroupId());
                groupCard.setTotalCourseTimes(groupCourseTimesInfo.getTotalCourseTimes());
                groupCard.setSurplusClassTimes(groupCourseTimesInfo.getSurplusClassTimes());
                groupCard.setCurrentClassTimes(groupCard.getTotalCourseTimes() - groupCard.getSurplusClassTimes());
                List<ClassGroupStudentMapper> groupStudents = groupStudentsMap.get(String.valueOf(teacherCourseGroup.getId()));
                if (!CollectionUtils.isEmpty(groupStudents)) {
                    List<String> userNames = groupStudents.stream().map(ClassGroupStudentMapper::getUserName).collect(Collectors.toList());
                    groupCard.setStudentNames(StringUtils.join(userNames, ","));
                    groupCard.setStudentIsFull(groupStudents.size() >= teacherCourseGroup.getMaxStudentNum() ? 1 : 0);
                } else {
                    groupCard.setStudentIsFull(3);
                }
                groupCards.add(groupCard);
            }
        }
        pageInfo.setRows(groupCards);
        return pageInfo;
    }

    @Override
    public PageInfo findTeacherCourseGroupsWithWeb(GroupQueryInfo queryInfo) {
        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<CourseGroupExtendDto> teacherCourseGroups = new ArrayList<>();
        int count = coursesGroupDao.countTeacherGroupsWithWeb(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            teacherCourseGroups = coursesGroupDao.findTeacherCourseGroupsWithWeb(params);
            List<String> groupIds = teacherCourseGroups.stream().map(e -> String.valueOf(e.getId())).collect(Collectors.toList());
            List<GroupBaseCourseInfoDto> groupStudentNums = groupDao.countGroupStudentNum(groupIds, GroupType.COMM);
            Map<String, Integer> groupStudentNumMap = groupStudentNums.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, GroupBaseCourseInfoDto::getStudentNum));

            List<GroupBaseCourseInfoDto> groupTotalClassesTimes = groupDao.countGroupTotalClassesTimes(groupIds, GroupType.COMM);
            Map<String, GroupBaseCourseInfoDto> groupBaseCourseInfoMap = groupTotalClassesTimes.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, e -> e));

            List<GroupBaseCourseInfoDto> groupFinishedClassesTimes = groupDao.countGroupFinishedClassesTimes(groupIds, GroupType.COMM);
            Map<String, Integer> groupFinishedClassesTimesMap = groupFinishedClassesTimes.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, e -> e.getCurrentClassTimes()));

            for (CourseGroupExtendDto teacherCourseGroup : teacherCourseGroups) {
                if (Objects.nonNull(groupStudentNumMap.get(teacherCourseGroup.getId().toString()))) {
                    teacherCourseGroup.setStudentNum(groupStudentNumMap.get(teacherCourseGroup.getId().toString()).intValue());
                }
                GroupBaseCourseInfoDto groupBaseCourseInfo = groupBaseCourseInfoMap.get(teacherCourseGroup.getId().toString());
                if (Objects.nonNull(groupBaseCourseInfo)) {
                    teacherCourseGroup.setTotalClassTimes(groupBaseCourseInfo.getTotalClassTimes());
                    teacherCourseGroup.setFirstClassesStartTime(groupBaseCourseInfo.getFirstClassesStartTime());
                    teacherCourseGroup.setLastClassesEndTime(groupBaseCourseInfo.getLastClassesEndTime());
                }
                if (Objects.nonNull(groupFinishedClassesTimesMap.get(teacherCourseGroup.getId().toString()))) {
                    teacherCourseGroup.setCurrentClassTimes(groupFinishedClassesTimesMap.get(teacherCourseGroup.getId().toString()).intValue());
                }
                if (Objects.nonNull(teacherCourseGroup.getTotalClassTimes())) {
                    teacherCourseGroup.setGroupClassesTotalDuration(teacherCourseGroup.getSingleClassMinutes() * teacherCourseGroup.getTotalClassTimes());
                }
                if (Objects.nonNull(teacherCourseGroup.getCurrentClassTimes())) {
                    teacherCourseGroup.setGroupClassesConsumeDuration(teacherCourseGroup.getSingleClassMinutes() * teacherCourseGroup.getCurrentClassTimes());
                }
            }
        }
        pageInfo.setRows(teacherCourseGroups);
        return pageInfo;
    }

    @Override
    public PageInfo findStudentCourseGroupsWithWeb(GroupQueryInfo queryInfo) {
        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<CourseGroupExtendDto> teacherCourseGroups = new ArrayList<>();
        int count = coursesGroupDao.countStudentGroupsWithWeb(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            teacherCourseGroups = coursesGroupDao.findStudentCourseGroupsWithWeb(params);
            List<Integer> teacherIds = teacherCourseGroups.stream().map(CourseGroupExtendDto::getTeacherId).collect(Collectors.toList());
            List<String> groupIds = teacherCourseGroups.stream().map(e -> String.valueOf(e.getId())).collect(Collectors.toList());
            List<GroupBaseCourseInfoDto> groupStudentNums = groupDao.countGroupStudentNum(groupIds, GroupType.COMM);
            Map<String, Integer> groupStudentNumMap = groupStudentNums.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, GroupBaseCourseInfoDto::getStudentNum));

            List<GroupBaseCourseInfoDto> groupTotalClassesTimes = groupDao.countGroupTotalClassesTimes(groupIds, GroupType.COMM);
            Map<String, GroupBaseCourseInfoDto> groupBaseCourseInfoMap = groupTotalClassesTimes.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, e -> e));

            List<GroupBaseCourseInfoDto> groupFinishedClassesTimes = groupDao.countGroupFinishedClassesTimes(groupIds, GroupType.COMM);
            Map<String, Integer> groupFinishedClassesTimesMap = groupFinishedClassesTimes.stream().collect(Collectors.toMap(GroupBaseCourseInfoDto::getId, e -> e.getCurrentClassTimes()));

            List<SimpleUserDto> teachers = teacherDao.findSimpleTeachers(teacherIds);
            Map<Integer, String> idNameMap = teachers.stream().collect(Collectors.toMap(SimpleUserDto::getUserId, SimpleUserDto::getUserName));

            for (CourseGroupExtendDto teacherCourseGroup : teacherCourseGroups) {
                if (Objects.nonNull(groupStudentNumMap.get(teacherCourseGroup.getId().toString()))) {
                    teacherCourseGroup.setStudentNum(groupStudentNumMap.get(teacherCourseGroup.getId().toString()).intValue());
                }
                GroupBaseCourseInfoDto groupBaseCourseInfo = groupBaseCourseInfoMap.get(teacherCourseGroup.getId().toString());
                if (Objects.nonNull(groupBaseCourseInfo)) {
                    teacherCourseGroup.setTotalClassTimes(groupBaseCourseInfo.getTotalClassTimes());
                    teacherCourseGroup.setFirstClassesStartTime(groupBaseCourseInfo.getFirstClassesStartTime());
                    teacherCourseGroup.setLastClassesEndTime(groupBaseCourseInfo.getLastClassesEndTime());
                }
                if (Objects.nonNull(groupFinishedClassesTimesMap.get(teacherCourseGroup.getId().toString()))) {
                    teacherCourseGroup.setCurrentClassTimes(groupFinishedClassesTimesMap.get(teacherCourseGroup.getId().toString()).intValue());
                }
                if (Objects.nonNull(teacherCourseGroup.getTotalClassTimes())) {
                    teacherCourseGroup.setGroupClassesTotalDuration(teacherCourseGroup.getSingleClassMinutes() * teacherCourseGroup.getTotalClassTimes());
                }
                if (Objects.nonNull(teacherCourseGroup.getCurrentClassTimes())) {
                    teacherCourseGroup.setGroupClassesConsumeDuration(teacherCourseGroup.getSingleClassMinutes() * teacherCourseGroup.getCurrentClassTimes());
                }
                teacherCourseGroup.setTeacherName(idNameMap.get(teacherCourseGroup.getTeacherId()));
            }
        }
        pageInfo.setRows(teacherCourseGroups);
        return pageInfo;
    }

    @Override
    public GroupHeadInfoDto getGroupHeadInfo(Long courseScheduleId) {
        if (Objects.isNull(courseScheduleId)) {
            throw new BizException("请选择课程");
        }
        CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
        if (Objects.isNull(courseSchedule)) {
            throw new BizException("课程不存在");
        }
        CoursesGroup coursesGroup = coursesGroupDao.get(Long.valueOf(courseSchedule.getMusicGroupId()));
        Teacher teacher = teacherDao.get(courseSchedule.getActualTeacherId());

        GroupHeadInfoDto groupHeadInfo = new GroupHeadInfoDto();
        groupHeadInfo.setGroupId(coursesGroup.getId().toString());
        groupHeadInfo.setGroupName(coursesGroup.getName());
        groupHeadInfo.setClassGroupId(courseSchedule.getClassGroupId());
        groupHeadInfo.setCourseStartDate(coursesGroup.getCoursesStartDate());
        groupHeadInfo.setCourseExpireDate(coursesGroup.getCoursesEndDate());
        groupHeadInfo.setTeacherName(teacher.getRealName());
        groupHeadInfo.setAvatar(teacher.getAvatar());
        groupHeadInfo.setIntroduction(teacher.getIntroduction());
        groupHeadInfo.setLectureNum(teacher.getLectureNum());

        return groupHeadInfo;
    }

    @Override
    public Map<String, Object> getGroupDetail(Long groupId) {
        if (Objects.isNull(groupId)) {
            throw new BizException("请指定课程组");
        }
        CoursesGroup coursesGroup = coursesGroupDao.get(groupId);
        Teacher teacher = teacherDao.get(coursesGroup.getTeacherId());
        List<String> subjectNames = subjectDao.findBySubIds(teacher.getSubjectId());
        Map<String, Object> result = new HashMap<>();
        result.put("groupId", groupId);
        result.put("groupName", coursesGroup.getName());
        result.put("courseStartDate", coursesGroup.getCoursesStartDate());
        result.put("courseEndDate", coursesGroup.getCoursesEndDate());
        result.put("teacherName", teacher.getRealName());
        result.put("avatar", teacher.getAvatar());
        result.put("introduction", teacher.getIntroduction());
        result.put("lectureNum", courseScheduleTeacherSalaryDao.countTeacherGiveLesson(coursesGroup.getTeacherId()));
        result.put("subjectNames", StringUtils.join(subjectNames, ","));
        List<CourseSchedule> groupNotStartCourses = courseScheduleDao.findGroupNotFinishCourses(groupId.toString(), GroupType.COMM);
        result.put("courseSchedules", groupNotStartCourses);
        Map<TeachModeEnum, Long> collect = groupNotStartCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getTeachMode, Collectors.counting()));
        result.put("totalClassesTimes", groupNotStartCourses.size());
        result.put("onlineClassesTimes", Objects.isNull(collect.get(TeachModeEnum.ONLINE)) ? 0 : collect.get(TeachModeEnum.ONLINE));
        result.put("offlineClassesTimes", Objects.isNull(collect.get(TeachModeEnum.OFFLINE)) ? 0 : collect.get(TeachModeEnum.OFFLINE));
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult buyCourseGroup(Integer userId, Long courseGroupId, boolean isUseBalancePayment) throws Exception {
        if (Objects.isNull(courseGroupId)) {
            throw new BizException("请指定课程组");
        }

        CoursesGroup coursesGroup = coursesGroupDao.lockGroup(courseGroupId);
        if (Objects.isNull(coursesGroup)) {
            throw new BizException("此课程组不存在");
        }
        if (coursesGroup.getStatus().equals(GroupStatusEnum.CANCEL)) {
            throw new BizException("课程已关闭，请联系老师了解详细情况。");
        }
        List<ClassGroupStudentMapper> groupStudents = classGroupStudentMapperDao.findGroupStudents(courseGroupId.toString(), GroupType.COMM, userId, null);
        if (!CollectionUtils.isEmpty(groupStudents)) {
            throw new BizException("您完成此课程组报名，无需重复报名。");
        }

        Date now = new Date();

        ClassGroup classGroup = classGroupDao.findByMusicGroupAndType(courseGroupId.toString(), GroupType.COMM.getCode());
        if (classGroup.getStudentNum() >= classGroup.getExpectStudentNum()) {
            throw new BizException("课程人数已满");
        }

        BigDecimal amount = coursesGroup.getTotalCoursesPrice();
        if (Objects.isNull(amount)) {
            amount = BigDecimal.ZERO;
        }

        StudentPaymentOrder studentPaymentOrder = new StudentPaymentOrder();
        studentPaymentOrder.setUserId(userId);
        studentPaymentOrder.setGroupType(GroupType.COMM);
        String orderNo = idGeneratorService.generatorId("payment") + "";
        studentPaymentOrder.setOrderNo(orderNo);
        studentPaymentOrder.setStatus(DealStatusEnum.ING);
        studentPaymentOrder.setType(OrderTypeEnum.COURSE_GROUP_BUY);
        studentPaymentOrder.setExpectAmount(amount);
        studentPaymentOrder.setMusicGroupId(coursesGroup.getId().toString());
        studentPaymentOrder.setActualAmount(studentPaymentOrder.getExpectAmount());
        studentPaymentOrder.setClassGroupId(classGroup.getId());
        studentPaymentOrder.setVersion(0);

        BigDecimal balance = BigDecimal.ZERO;
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return this.orderCallback(studentPaymentOrder);
        } else {
            studentPaymentOrderService.insert(studentPaymentOrder);

            if (isUseBalancePayment || studentPaymentOrder.getExpectAmount().doubleValue() == 0) {
                SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(userId);
                if (userCashAccount == null) {
                    throw new BizException("用户账户找不到");
                }
                studentPaymentOrder.setPaymentChannel("BALANCE");
                if (userCashAccount.getBalance().subtract(studentPaymentOrder.getExpectAmount()).doubleValue() >= 0) {
                    studentPaymentRouteOrderService.addRouteOrder(orderNo, coursesGroup.getOrganId(), studentPaymentOrder.getExpectAmount());

                    // 更新订单信息
                    studentPaymentOrder.setActualAmount(new BigDecimal(0));
                    studentPaymentOrder.setBalancePaymentAmount(studentPaymentOrder.getExpectAmount());
                    studentPaymentOrder.setStatus(DealStatusEnum.SUCCESS);
                    studentPaymentOrder.setUpdateTime(now);
                    studentPaymentOrder.setRoutingOrganId(coursesGroup.getOrganId());

                    sysUserCashAccountService.updateBalance(userId, studentPaymentOrder.getExpectAmount().negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "对外课程购买");

                    this.orderCallback(studentPaymentOrder);

                    Map<String, Object> result = new HashMap<>();
                    result.put("orderNo", studentPaymentOrder.getOrderNo());

                    return BaseController.succeed(result);
                } else {
                    if (userCashAccount.getBalance().doubleValue() > 0) {
                        balance = userCashAccount.getBalance();
                        sysUserCashAccountService.updateBalance(userId, balance.negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "对外课程购买");
                        amount = studentPaymentOrder.getExpectAmount().subtract(userCashAccount.getBalance());
                        studentPaymentOrder.setActualAmount(amount);
                        studentPaymentOrder.setBalancePaymentAmount(userCashAccount.getBalance());
                    } else {
                        studentPaymentOrder.setBalancePaymentAmount(new BigDecimal(0));
                    }
                }
            }

            String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
            Map<String, BigDecimal> classFee = new HashMap<>();
            classFee.put("course", amount);
            classFee.put("instrument", BigDecimal.ZERO);
            classFee.put("accessories", BigDecimal.ZERO);
            classFee.put("other", BigDecimal.ZERO);
            Map<String, Object> payMap = payService.getPayMap(
                    amount,
                    balance,
                    orderNo,
                    baseApiUrl + "/api-student/studentOrder/notify",
                    baseApiUrl + "/api-student/studentOrder/paymentResult?orderNo=" + orderNo,
                    "对外课程组购买",
                    coursesGroup.getName(),
                    coursesGroup.getOrganId(),
                    "outGroupBuy"
            );

            studentPaymentOrder.setMerNos((String) payMap.get("routingMerNos"));
            studentPaymentOrder.setPaymentChannel((String) payMap.get("type"));
            studentPaymentOrder.setUpdateTime(now);
            studentPaymentOrderService.update(studentPaymentOrder);

            return BaseController.succeed(payMap);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult orderCallback(StudentPaymentOrder order) {
        if (order.getExpectAmount().compareTo(BigDecimal.ZERO) > 0) {
            StudentPaymentOrder studentPaymentOrder = studentPaymentOrderDao.get(order.getId());
            if (!studentPaymentOrder.getStatus().equals(DealStatusEnum.ING)) {
                return BaseController.succeed();
            }
            SysUserCashAccount sysUserCashAccount = sysUserCashAccountService.get(order.getUserId());
            List<StudentPaymentOrder> userGroupOrders = studentPaymentOrderDao.findUserGroupOrders(order.getUserId(), order.getMusicGroupId(), order.getGroupType(), null);
            Map<DealStatusEnum, Long> statusOrderNumMap = userGroupOrders.stream().collect(Collectors.groupingBy(StudentPaymentOrder::getStatus, Collectors.counting()));
            Long successOrderNum = statusOrderNumMap.get(DealStatusEnum.SUCCESS);
            if (Objects.nonNull(successOrderNum) && successOrderNum > 0) {
                if (order.getStatus().equals(DealStatusEnum.FAILED) && Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                    sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
                }
                if (order.getStatus().equals(DealStatusEnum.SUCCESS)) {
                    sysUserCashAccountService.updateBalance(order.getUserId(), order.getExpectAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "重复支付，退还余额");
                    order.setMemo("重复支付,进入余额");
                }
                studentPaymentOrderDao.update(order);
                return BaseController.succeed();
            }

            Long ingOrderNum = statusOrderNumMap.get(DealStatusEnum.ING);
            if (Objects.nonNull(ingOrderNum) && ingOrderNum > 1 && order.getStatus().equals(DealStatusEnum.FAILED)) {
                if (Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                    sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
                }
                studentPaymentOrderDao.update(order);
                return BaseController.succeed();
            }

            if (order.getStatus().equals(DealStatusEnum.SUCCESS)) {
                courseScheduleDao.updateGroupCourseLock(order.getMusicGroupId(), GroupType.PRACTICE, 0);
            } else {
                if (Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                    sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
                }
                studentPaymentOrderDao.update(order);
                CoursesGroup coursesGroup = coursesGroupDao.get(Long.valueOf(order.getMusicGroupId()));
                coursesGroup.setStatus(GroupStatusEnum.CANCEL);
                coursesGroupDao.update(coursesGroup);
                groupService.deleteGroupOtherInfo(order.getMusicGroupId(), GroupType.PRACTICE);
                return BaseController.succeed();
            }
            studentPaymentOrderDao.update(order);

            //插入缴费明细
            //收入
            SysUserCashAccountDetail sysUserIncomeCashAccountDetail = new SysUserCashAccountDetail();
            sysUserIncomeCashAccountDetail.setUserId(order.getUserId());
            sysUserIncomeCashAccountDetail.setType(PlatformCashAccountDetailTypeEnum.RECHARGE);
            sysUserIncomeCashAccountDetail.setStatus(DealStatusEnum.SUCCESS);
            sysUserIncomeCashAccountDetail.setAmount(order.getActualAmount());
            sysUserIncomeCashAccountDetail.setBalance(sysUserCashAccount.getBalance().add(order.getActualAmount()));
            sysUserIncomeCashAccountDetail.setAttribute(order.getTransNo());
            sysUserIncomeCashAccountDetail.setChannel(studentPaymentOrder.getPaymentChannel());
            sysUserIncomeCashAccountDetail.setComAmount(studentPaymentOrder.getComAmount());
            sysUserIncomeCashAccountDetail.setPerAmount(studentPaymentOrder.getPerAmount());

            //支出
            SysUserCashAccountDetail sysUserExpendCashAccountDetail = new SysUserCashAccountDetail();
            sysUserExpendCashAccountDetail.setUserId(order.getUserId());
            sysUserExpendCashAccountDetail.setType(PlatformCashAccountDetailTypeEnum.PAY_FEE);
            sysUserExpendCashAccountDetail.setStatus(DealStatusEnum.SUCCESS);
            sysUserExpendCashAccountDetail.setAmount(order.getActualAmount().negate());
            sysUserExpendCashAccountDetail.setBalance(sysUserCashAccount.getBalance());
            sysUserExpendCashAccountDetail.setAttribute(order.getTransNo());
            sysUserExpendCashAccountDetail.setChannel(studentPaymentOrder.getPaymentChannel());

            if (studentPaymentOrder.getComAmount() != null) {
                sysUserIncomeCashAccountDetail.setComAmount(studentPaymentOrder.getComAmount().negate());
            }
            if (studentPaymentOrder.getPerAmount() != null) {
                sysUserExpendCashAccountDetail.setPerAmount(studentPaymentOrder.getPerAmount().negate());
            }

            sysUserCashAccountDetailService.insert(sysUserIncomeCashAccountDetail);
            sysUserCashAccountDetailService.insert(sysUserExpendCashAccountDetail);

            CoursesGroup coursesGroup = coursesGroupDao.get(Long.valueOf(order.getMusicGroupId()));
            if (!coursesGroup.getStatus().equals(GroupStatusEnum.NORMAL)) {
                order.setVersion(order.getVersion() + 1);
                order.setMemo("支付成功，但课程组不处于正常状态");
                studentPaymentOrderDao.update(order);

                sysUserCashAccountService.updateBalance(order.getUserId(), order.getExpectAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "支付成功，但课程组不处于正常状态");

                return BaseController.succeed();
            }
        }
        CoursesGroup coursesGroup = coursesGroupDao.get(Long.valueOf(order.getMusicGroupId()));
        ClassGroup classGroup = classGroupDao.findByMusicGroupAndType(order.getMusicGroupId(), GroupType.COMM.getCode());

        ClassGroupStudentMapper classGroupStudentMapper = new ClassGroupStudentMapper();
        classGroupStudentMapper.setMusicGroupId(order.getMusicGroupId());
        classGroupStudentMapper.setClassGroupId(classGroup.getId());
        classGroupStudentMapper.setUserId(order.getUserId());
        classGroupStudentMapper.setStatus(ClassGroupStudentStatusEnum.NORMAL);
        classGroupStudentMapper.setGroupType(GroupType.COMM);
        classGroupStudentMapperDao.insert(classGroupStudentMapper);

        List<ClassGroupStudentMapper> classGroupStudents = classGroupStudentMapperDao.findByClassGroup(classGroup.getId());
        if (!CollectionUtils.isEmpty(classGroupStudents)) {
            classGroup.setStudentNum(classGroupStudents.size());
            classGroupDao.update(classGroup);
        }
        if (!CollectionUtils.isEmpty(classGroupStudents) && classGroupStudents.size() >= classGroup.getExpectStudentNum()) {
            coursesGroup.setStatus(GroupStatusEnum.NORMAL);
            coursesGroupDao.update(coursesGroup);
        }

        List<CourseScheduleStudentPayment> courseScheduleStudentPayments = new ArrayList<>();
        List<CourseSchedule> groupNotStartCourses = courseScheduleDao.findGroupNotFinishCourses(order.getMusicGroupId(), GroupType.COMM);
        if (CollectionUtils.isEmpty(groupNotStartCourses)) {
            throw new BizException("课程已关闭，请联系老师了解详细情况。");
        }
        BigDecimal singleCoursePrice = order.getExpectAmount().divide(new BigDecimal(groupNotStartCourses.size()), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);
        for (CourseSchedule courseSchedule : groupNotStartCourses) {
            //学生缴费记录
            CourseScheduleStudentPayment courseScheduleStudentPayment = new CourseScheduleStudentPayment();
            courseScheduleStudentPayment.setGroupType(GroupType.COMM);
            courseScheduleStudentPayment.setMusicGroupId(order.getMusicGroupId());
            courseScheduleStudentPayment.setCourseScheduleId(courseSchedule.getId());
            courseScheduleStudentPayment.setUserId(order.getUserId());
            courseScheduleStudentPayment.setExpectPrice(singleCoursePrice);
            courseScheduleStudentPayment.setOriginalPrice(singleCoursePrice);
            courseScheduleStudentPayment.setActualPrice(singleCoursePrice);
            courseScheduleStudentPayment.setClassGroupId(classGroup.getId());
            courseScheduleStudentPayments.add(courseScheduleStudentPayment);
        }
        courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);

        try {
            courseScheduleService.checkNewCourseSchedules(groupNotStartCourses, false,false);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            String errMessage;

            String courseName = e.getMessage().substring(e.getMessage().indexOf(")-") + 2);
            courseName = courseName.substring(0, courseName.indexOf("("));
            String courseTime = e.getMessage().substring(e.getMessage().indexOf("在") + 1);
            courseTime = courseTime.substring(0, courseTime.indexOf("至"));
            errMessage = "现在课程：";
            errMessage += courseName;
            errMessage += "<br/>冲突时间：";
            errMessage += courseTime;

            return BaseController.failed(HttpStatus.FOUND, errMessage);
        }
        
        Map<Integer,String> userRoleMap = new HashMap<Integer, String>();
        userRoleMap.put(coursesGroup.getTeacherId(), "");
        userRoleMap.put(order.getUserId(), "");
        
        imGroupService.create(classGroup.getId().longValue(), coursesGroup.getTeacherId(), classGroup.getName(), classGroup.getName(), classGroup.getName(), classGroup.getName(), null, "COMM");
        imGroupMemberService.join(classGroup.getId().longValue(), userRoleMap);
        imUserFriendService.refreshClassImUserFriend(classGroup.getId());
        return BaseController.succeed();
    }

    @Override
    public void noStudentsCourseGroupRemind() {
        Date startDate = DateUtil.addDays(new Date(), 1);
        List<CoursesGroup> noStudentsGroups = coursesGroupDao.findNoStudentsGroups(startDate);
        if (CollectionUtils.isEmpty(noStudentsGroups)) {
            return;
        }
        for (CoursesGroup noStudentsGroup : noStudentsGroups) {
            Map<Integer, String> userMap = new HashMap<>();
            userMap.put(noStudentsGroup.getTeacherId(), noStudentsGroup.getTeacherId().toString());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.COMM_NO_STUDENTS_PUSH,
                    userMap, null, 0, null, "TEACHER", noStudentsGroup.getName());
        }
    }

    @Override
    public void finishCourseGroup() {
        List<CoursesGroup> noCoursesGroups = coursesGroupDao.findNoCoursesGroups();
        if (CollectionUtils.isEmpty(noCoursesGroups)) {
            return;
        }
        List<Long> groupIds = noCoursesGroups.stream().map(CoursesGroup::getId).collect(Collectors.toList());
        coursesGroupDao.updateGroupStatus(groupIds, GroupStatusEnum.FINISH);
    }
}
