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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.CourseScheduleStudentListDto;
import com.ym.mec.biz.dal.dto.SimpleUserDto;
import com.ym.mec.biz.dal.dto.VipGroupGiveCourseSortDto;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.*;
import com.ym.mec.biz.dal.page.CourseScheduleStudentPaymentQueryInfo;
import com.ym.mec.biz.service.CourseScheduleStudentPaymentService;
import com.ym.mec.biz.service.StudentRegistrationService;
import com.ym.mec.biz.service.SysUserCashAccountService;
import com.ym.mec.common.constant.CommonConstants;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.redis.service.RedisCache;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.util.date.DateUtil;
import com.ym.mec.util.collection.MapUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

@Service
public class CourseScheduleStudentPaymentServiceImpl extends BaseServiceImpl<Long, CourseScheduleStudentPayment>  implements CourseScheduleStudentPaymentService {

	@Autowired
	private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
	@Autowired
	private VipGroupDao vipGroupDao;
	@Autowired
	private VipGroupActivityDao vipGroupActivityDao;
	@Autowired
	private CourseScheduleDao courseScheduleDao;
	@Autowired
	private ClassGroupStudentMapperDao classGroupStudentMapperDao;
	@Autowired
	private SysUserCashAccountService sysUserCashAccountService;
	@Autowired
	private MusicGroupPaymentCalenderDao musicGroupPaymentCalenderDao;
	@Autowired
	private MusicGroupPaymentCalenderDetailDao musicGroupPaymentCalenderDetailDao;
	@Autowired
	private MusicGroupPaymentCalenderCourseSettingsDao musicGroupPaymentCalenderCourseSettingsDao;
	@Autowired
	private VipGroupDefaultClassesUnitPriceDao vipGroupDefaultClassesUnitPriceDao;
	@Autowired
	private MusicGroupPaymentStudentCourseDetailDao musicGroupPaymentStudentCourseDetailDao;
	@Autowired
	private TeacherDao teacherDao;
	@Autowired
	private StudentAttendanceDao studentAttendanceDao;
	@Autowired
	private StudentDao studentDao;
	@Autowired
	private SubjectDao subjectDao;
	@Autowired
	private StudentRegistrationService studentRegistrationService;
	@Autowired
	private MusicGroupStudentClassAdjustDao musicGroupStudentClassAdjustDao;
	@Autowired
	private SysConfigDao sysConfigDao;

	@Override
	public BaseDAO<Long, CourseScheduleStudentPayment> getDAO() {
		return courseScheduleStudentPaymentDao;
	}

	@Override
	public int createCourseScheduleStudentPaymentForVipGroup(Long vipGroupId, Integer userId) {
		VipGroup vipGroupApplyBaseInfoDto=vipGroupDao.get(vipGroupId);

		if(Objects.isNull(vipGroupApplyBaseInfoDto)){
			throw new BizException("未找到相关vip课程");
		}

		VipGroupDefaultClassesUnitPrice vipGroupDefaultClassesUnitPrice = vipGroupDefaultClassesUnitPriceDao.getByVipGroup(vipGroupId);
		if(Objects.isNull(vipGroupDefaultClassesUnitPrice)){
			throw new BizException("课程单价设置错误");
		}

		//获取活动信息
		VipGroupActivity vipGroupActivity = vipGroupActivityDao.get(vipGroupApplyBaseInfoDto.getVipGroupActivityId().intValue());

		List<CourseSchedule> vipGroupCourseSchedules=courseScheduleDao.findGroupCourseSchedules(vipGroupId,GroupType.VIP.getCode());

		if(CollectionUtils.isEmpty(vipGroupCourseSchedules)){
			throw new BizException("未获取到排课信息");
		}

        int giveClassTimes=0;
        if(vipGroupApplyBaseInfoDto.getStatus().equals(VipGroupStatusEnum.APPLYING)
                &&Objects.nonNull(vipGroupActivity)
                &&vipGroupActivity.getType().equals(VipGroupActivityTypeEnum.GIVE_CLASS)
                &&vipGroupCourseSchedules.size()>=Integer.parseInt(vipGroupActivity.getAttribute1())){
            giveClassTimes=Integer.parseInt(vipGroupActivity.getAttribute2());
        }

		List<CourseScheduleStudentPayment> courseScheduleStudentPayments=new ArrayList<>();
		Map<TeachModeEnum, List<CourseSchedule>> courseScheduleGroupByTeachMode = vipGroupCourseSchedules.stream().collect(Collectors.groupingBy(CourseSchedule::getTeachMode));
		for(TeachModeEnum teachModeEnum:courseScheduleGroupByTeachMode.keySet()){
			List<CourseSchedule> courseSchedules = courseScheduleGroupByTeachMode.get(teachModeEnum)
					.stream()
					.sorted(Comparator.comparing(CourseSchedule::getStartClassTime)).collect(Collectors.toList());
			for(int i=0;i<courseSchedules.size();i++) {
				//创建学生课程应缴费记录
				CourseScheduleStudentPayment courseScheduleStudentPayment = new CourseScheduleStudentPayment();
				courseScheduleStudentPayment.setUserId(userId);
	            courseScheduleStudentPayment.setGroupType(courseSchedules.get(i).getGroupType());
	            courseScheduleStudentPayment.setMusicGroupId(courseSchedules.get(i).getMusicGroupId());
				courseScheduleStudentPayment.setCourseScheduleId(courseSchedules.get(i).getId());
				courseScheduleStudentPayment.setClassGroupId(courseSchedules.get(i).getClassGroupId());

				if (teachModeEnum == TeachModeEnum.ONLINE) {
					//学生线上单节课应缴费计算
					switch (vipGroupActivity.getType()) {
						case BASE_ACTIVITY:
							courseScheduleStudentPayment.setExpectPrice(vipGroupApplyBaseInfoDto.getOnlineClassesUnitPrice());
							break;
						case DISCOUNT:
							courseScheduleStudentPayment.setExpectPrice(
									vipGroupApplyBaseInfoDto.getOnlineClassesUnitPrice().multiply(
											new BigDecimal(vipGroupActivity.getAttribute1()))
											.divide(new BigDecimal(100), CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_HALF_UP)
							);
							break;
						case GIVE_CLASS:
							if (vipGroupApplyBaseInfoDto.getGiveTeachMode() == teachModeEnum
									&& i >= (courseSchedules.size()-giveClassTimes)) {
								courseScheduleStudentPayment.setExpectPrice(new BigDecimal(0));
							} else {
								courseScheduleStudentPayment.setExpectPrice(vipGroupApplyBaseInfoDto.getOnlineClassesUnitPrice());
							}
							break;
						default:
							throw new BizException("活动类型错误!");
					}
				}else{
					//学生线下单节课应缴费计算
					switch (vipGroupActivity.getType()) {
						case BASE_ACTIVITY:
							courseScheduleStudentPayment.setExpectPrice(vipGroupApplyBaseInfoDto.getOfflineClassesUnitPrice());
							break;
						case DISCOUNT:
							courseScheduleStudentPayment.setExpectPrice(
									vipGroupApplyBaseInfoDto.getOfflineClassesUnitPrice().multiply(
											new BigDecimal(vipGroupActivity.getAttribute1()))
											.divide(new BigDecimal(100), CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_HALF_UP)
							);
							break;
						case GIVE_CLASS:
							if (vipGroupApplyBaseInfoDto.getGiveTeachMode() == teachModeEnum
                                    && i >= (courseSchedules.size()-giveClassTimes)) {
								courseScheduleStudentPayment.setExpectPrice(new BigDecimal(0));
							} else {
								courseScheduleStudentPayment.setExpectPrice(vipGroupApplyBaseInfoDto.getOfflineClassesUnitPrice());
							}
							break;
						default:
							throw new BizException("活动类型错误!");
					}
				}
                courseScheduleStudentPayment.setOriginalPrice(TeachModeEnum.ONLINE.equals(teachModeEnum)?vipGroupDefaultClassesUnitPrice.getOnlineClassesUnitPrice():vipGroupDefaultClassesUnitPrice.getOfflineClassesUnitPrice());
				courseScheduleStudentPayment.setActualPrice(courseScheduleStudentPayment.getExpectPrice());
				courseScheduleStudentPayments.add(courseScheduleStudentPayment);
			}
		}
		return courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);
	}

    @Override
    public int deleteStudentCourseSchedule(Integer userId, List<CourseSchedule> courseScheduleList) {
		if(courseScheduleList.size() == 0){
			return 0;
		}
		List<Long> courseScheduleIdList = courseScheduleList.stream().map(CourseSchedule :: getId).collect(Collectors.toList());
		return courseScheduleStudentPaymentDao.deleteStudentCourseSchedule(userId,courseScheduleIdList);
    }

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void createCourseScheduleStudentPaymentByCourseSchedules(List<CourseSchedule> courseSchedules) {
		if(CollectionUtils.isEmpty(courseSchedules)){
			return;
		}

		List<CourseScheduleStudentPayment> courseScheduleStudentPayments=new ArrayList<>();

		BigDecimal zeroPrice=new BigDecimal(0);

		//班级编号列表
		List<Integer> classGroupIds = courseSchedules.stream().map(CourseSchedule::getClassGroupId).collect(Collectors.toList());
		Map<Integer, List<CourseSchedule>> courseSchedulesByClassGroupMap = courseSchedules.stream().collect(Collectors.groupingBy(CourseSchedule::getClassGroupId));

		//班级学生对应列表
		List<ClassGroupStudentMapper> classGroupStudentMappers = classGroupStudentMapperDao.findByClassGroups(classGroupIds);
		Map<Integer, List<ClassGroupStudentMapper>> studentsByClassMap = classGroupStudentMappers.stream().collect(Collectors.groupingBy(ClassGroupStudentMapper::getClassGroupId));

		courseSchedulesByClassGroupMap.forEach((classGroupId,courseSchedulesByClassGroup)->{
			List<ClassGroupStudentMapper> studentsWithClassGroup = studentsByClassMap.get(classGroupId);
			if(CollectionUtils.isEmpty(studentsWithClassGroup)){
				return;
			}
			studentsWithClassGroup.forEach(classGroupStudent->{
				VipGroup vipGroup=null;
				if(classGroupStudent.getGroupType().equals(GroupType.VIP)){
					vipGroup=vipGroupDao.get(Long.valueOf(classGroupStudent.getMusicGroupId()));
				}
				for (CourseSchedule courseSchedule : courseSchedulesByClassGroup) {
					int i = courseScheduleStudentPaymentDao.checkStudentHaveCourse(courseSchedule.getId(), classGroupStudent.getUserId());
					if(i>0){
						continue;
					}
					if(courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.VIP)){
						CourseScheduleStudentPayment courseScheduleStudentPayment=new CourseScheduleStudentPayment(
								courseSchedule.getId(),
								classGroupStudent.getUserId(),
								courseSchedule.getTeachMode().equals(TeachModeEnum.ONLINE)?vipGroup.getOnlineClassesUnitPrice():vipGroup.getOfflineClassesUnitPrice(),
								courseSchedule.getClassGroupId()
						);
						courseScheduleStudentPayment.setGroupType(classGroupStudent.getGroupType());
						courseScheduleStudentPayment.setMusicGroupId(classGroupStudent.getMusicGroupId());
						List<CourseScheduleStudentPayment> otherPayments = courseScheduleStudentPaymentDao.findByCourseSchedule(courseSchedule.getId());
						if(!CollectionUtils.isEmpty(otherPayments)){
							courseScheduleStudentPayment.setExpectPrice(otherPayments.get(0).getExpectPrice());
						}
						if(Objects.isNull(courseScheduleStudentPayment.getExpectPrice())){
							courseScheduleStudentPayment.setExpectPrice(zeroPrice);
						}
						sysUserCashAccountService.appendCourseBalance(classGroupStudent.getUserId(), courseScheduleStudentPayment.getExpectPrice().negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE,"学生加课扣除课程余额");
						courseScheduleStudentPayments.add(courseScheduleStudentPayment);
					}else{
						CourseScheduleStudentPayment courseScheduleStudentPayment = new CourseScheduleStudentPayment(
								courseSchedule.getId(),
								classGroupStudent.getUserId(),
								zeroPrice,
								courseSchedule.getClassGroupId());
						courseScheduleStudentPayment.setGroupType(classGroupStudent.getGroupType());
						courseScheduleStudentPayment.setMusicGroupId(classGroupStudent.getMusicGroupId());
						courseScheduleStudentPayments.add(courseScheduleStudentPayment);
					}
				}
			});
		});
		if(!CollectionUtils.isEmpty(courseScheduleStudentPayments)){
			courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);
		}
	}

	@Override
	public int batchInsert(List<CourseScheduleStudentPayment> courseScheduleStudentPayments) {
		return courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateVipGiveCourse(Long vipGroupId) {
		List<VipGroupGiveCourseSortDto> vipGroupSortCourseByGroup = courseScheduleStudentPaymentDao.findVipGroupSortCourseByGroup(vipGroupId.toString());
		Map<Integer, List<VipGroupGiveCourseSortDto>> userCourseMap = vipGroupSortCourseByGroup.stream().collect(Collectors.groupingBy(VipGroupGiveCourseSortDto::getUserId));

        BigDecimal zero = new BigDecimal("0.00");

		List<CourseScheduleStudentPayment> needUpdatePayments=new ArrayList<>();
		for (Map.Entry<Integer, List<VipGroupGiveCourseSortDto>> integerListEntry : userCourseMap.entrySet()) {
			List<VipGroupGiveCourseSortDto> courses = integerListEntry.getValue();
			Map<BigDecimal, Long> expectPriceCountMap = courses.stream()
					.collect(Collectors.groupingBy(VipGroupGiveCourseSortDto::getExpectPrice, Collectors.counting()));
			Long zeroPriceNum = expectPriceCountMap.get(zero);
			if(Objects.isNull(zeroPriceNum)||zeroPriceNum<=0){
				break;
			}

			List<VipGroupGiveCourseSortDto> zeroExpectPricePayments = courses.stream()
					.filter(vipGroupGiveCourseSortDto -> vipGroupGiveCourseSortDto.getExpectPrice().compareTo(zero)==0)
					.collect(Collectors.toList());

			courses.sort(Comparator.comparing(VipGroupGiveCourseSortDto::getCourseStartTime).reversed());
			for (int i=0;i<courses.size();i++) {
				if(i>zeroExpectPricePayments.size()-1){
					break;
				}

				VipGroupGiveCourseSortDto vipGroupGiveCourseSortDto=courses.get(i);

				if(!vipGroupGiveCourseSortDto.getId().equals(zeroExpectPricePayments.get(i).getId())
						&&!vipGroupGiveCourseSortDto.getTeachMode().equals(zeroExpectPricePayments.get(i).getTeachMode())){
					throw new BizException("不支持将不同教学形式的课程调整到赠送课时后");
				}

				if(!vipGroupGiveCourseSortDto.getId().equals(zeroExpectPricePayments.get(i).getId())){
					needUpdatePayments.add(new CourseScheduleStudentPayment(vipGroupGiveCourseSortDto.getId(),zeroExpectPricePayments.get(i).getCourseScheduleId()));
					needUpdatePayments.add(new CourseScheduleStudentPayment(zeroExpectPricePayments.get(i).getId(),vipGroupGiveCourseSortDto.getCourseScheduleId()));
				}
			}
		}

		if(!CollectionUtils.isEmpty(needUpdatePayments)){
			courseScheduleStudentPaymentDao.batchUpdate(needUpdatePayments);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void createForMusicGroup(List<CourseSchedule> courseSchedules, List<Integer> studentIds,Integer musicGroupStudentClassAdjustId) {
		//获取提交的合班申请
		MusicGroupStudentClassAdjust classAdjust = musicGroupStudentClassAdjustDao.get(musicGroupStudentClassAdjustId);
		List<Long> studentPaymentIds = JSON.parseArray(classAdjust.getStudentPaymentIds(), Long.class);
		String batchNo = classAdjust.getBatchNo();

		//获取默认的排课时长
//		classAdjust.getDefaultCourseTypeMinute();
//		String courseDefaultMinutes = sysConfigDao.findConfigValue("music_course_default_minutes");
		JSONObject jsonObject = JSON.parseObject(classAdjust.getDefaultCourseTypeMinute());
		List<CourseScheduleStudentPayment> totalCourseScheduleStudentPayments = new ArrayList<>();
		for (Integer studentId : studentIds) {
			List<MusicGroupPaymentStudentCourseDetail> musicGroupPaymentStudentCourseDetails = null;
			BigDecimal typeCourseTotalOriginalPrice = new BigDecimal("0"), typeCourseTotalCurrentPrice = new BigDecimal("0");
			List<CourseScheduleStudentPayment> oldCourseScheduleStudentPayments = new ArrayList<>();
			List<CourseScheduleStudentPayment> newCourseScheduleStudentPayments = new ArrayList<>();
			for (int i = 0; i < courseSchedules.size(); i++) {
				CourseSchedule courseSchedule = courseSchedules.get(i);
				//优先处理之前的剩余课程
				//获取调班之前，学员剩余的课程列表
				if(studentPaymentIds != null && studentPaymentIds.size() > 0){
					List<CourseScheduleStudentPayment> studentPayments = courseScheduleStudentPaymentDao.queryByIdsAndStudentId(studentPaymentIds,studentId,courseSchedule.getType().getCode());
					if(studentPayments != null && studentPayments.size() > 0){
						CourseScheduleStudentPayment studentPayment = new CourseScheduleStudentPayment();
						try {
							BeanUtils.copyProperties(studentPayment, studentPayments.get(0));
						} catch (Exception e) {
							throw new BizException("克隆对象出错", e);
						}
						studentPaymentIds.remove(studentPayment.getId());
						studentPayment.setCourseScheduleId(courseSchedule.getId());
						studentPayment.setClassGroupId(courseSchedule.getClassGroupId());
						studentPayment.setOpenPlayMidi(null);
						studentPayment.setSettlementTime(null);
						studentPayment.setExamSongDownloadJson(null);
						studentPayment.setId(null);
						oldCourseScheduleStudentPayments.add(studentPayment);
						totalCourseScheduleStudentPayments.add(studentPayment);
						continue ;
					}
				}
				//处理缴费的排课信息
				if(musicGroupPaymentStudentCourseDetails == null){
					musicGroupPaymentStudentCourseDetails = musicGroupPaymentStudentCourseDetailDao.getUnUseWithStudentAndCourseTypeAndCourseMinutes(batchNo, studentId,courseSchedule.getType());
				}
				if(musicGroupPaymentStudentCourseDetails == null || musicGroupPaymentStudentCourseDetails.size() == 0){
					throw new BizException("学员缴费详情获取失败");
				}
				//获取合班时的缴费项目
				Set<Long> calenderIds = musicGroupPaymentCalenderDao.findByBatchNoAndStudentId(batchNo,studentId);
				List<MusicGroupPaymentCalenderCourseSettings> musicGroupPaymentCalenderCourseSettings = musicGroupPaymentCalenderCourseSettingsDao.getWithPaymentCalendersAndCourseType(calenderIds, courseSchedule.getType());
				MusicGroupPaymentCalenderCourseSettings courseSetting = musicGroupPaymentCalenderCourseSettings.get(0);
				//课程每分钟原价
				BigDecimal unitMinuteOriginalPrice = courseSetting.getCourseOriginalPrice().divide(new BigDecimal(courseSetting.getCourseTotalMinuties()), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);

				//课程每分钟现价
				BigDecimal unitMinuteCurrentPrice = courseSetting.getCourseCurrentPrice().divide(new BigDecimal(courseSetting.getCourseTotalMinuties()), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);

				//默认课程时长
				int courseDuration = Integer.parseInt(jsonObject.get(courseSchedule.getType().getCode()).toString());
				BigDecimal courseOriginalPrice = unitMinuteOriginalPrice.multiply(new BigDecimal(courseDuration)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN);
				typeCourseTotalOriginalPrice = typeCourseTotalOriginalPrice.add(courseOriginalPrice);
				//课程现价
				BigDecimal courseCurrentPrice = unitMinuteCurrentPrice.multiply(new BigDecimal(courseDuration)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN);
				typeCourseTotalCurrentPrice = typeCourseTotalCurrentPrice.add(courseCurrentPrice);
				CourseScheduleStudentPayment cssp = new CourseScheduleStudentPayment();
				cssp.setGroupType(courseSchedule.getGroupType());
				cssp.setMusicGroupId(courseSchedule.getMusicGroupId());
				cssp.setCourseScheduleId(courseSchedule.getId());
				cssp.setClassGroupId(courseSchedule.getClassGroupId());
				MusicGroupPaymentCalender musicGroupPaymentCalender = musicGroupPaymentCalenderDao.get(calenderIds.iterator().next());
				cssp.setBatchNo(musicGroupPaymentCalender.getBatchNo());
				cssp.setUserId(studentId);
				cssp.setOriginalPrice(courseOriginalPrice);
				cssp.setExpectPrice(courseCurrentPrice);
				cssp.setActualPrice(BigDecimal.ZERO);
				newCourseScheduleStudentPayments.add(cssp);
				if(courseSchedules.size() - 1 == i){
					CourseScheduleStudentPayment scheduleStudentPayment = newCourseScheduleStudentPayments.get(0);
					scheduleStudentPayment.setOriginalPrice(scheduleStudentPayment.getOriginalPrice().add(courseSetting.getCourseOriginalPrice().subtract(typeCourseTotalOriginalPrice)));
					scheduleStudentPayment.setExpectPrice(scheduleStudentPayment.getExpectPrice().add(courseSetting.getCourseCurrentPrice().subtract(typeCourseTotalCurrentPrice)));
					totalCourseScheduleStudentPayments.addAll(newCourseScheduleStudentPayments);
				}
				//获取课程类型默认排课时长
				int typeCourseDuration = Integer.parseInt(jsonObject.get(courseSchedule.getType().getCode()).toString());
				for (MusicGroupPaymentStudentCourseDetail musicGroupPaymentStudentCourseDetail : musicGroupPaymentStudentCourseDetails) {
					if(typeCourseDuration > musicGroupPaymentStudentCourseDetail.getTotalCourseMinutes()){
						musicGroupPaymentStudentCourseDetail.setUsedCourseMinutes(musicGroupPaymentStudentCourseDetail.getTotalCourseMinutes());
					}else{
						musicGroupPaymentStudentCourseDetail.setUsedCourseMinutes(musicGroupPaymentStudentCourseDetail.getUsedCourseMinutes() + typeCourseDuration);
					}
				}
			}
			if(musicGroupPaymentStudentCourseDetails != null && musicGroupPaymentStudentCourseDetails.size() > 0){
				musicGroupPaymentStudentCourseDetailDao.batchUpdate(musicGroupPaymentStudentCourseDetails);
			}
		}
		courseScheduleStudentPaymentDao.batchInsert(totalCourseScheduleStudentPayments);
	}


	@Override
	@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
	public void createForMusicGroup(String musicGroupId, List<CourseSchedule> courseSchedules, List<Integer> studentIds) {
		Map<CourseSchedule.CourseScheduleType, Integer> courseTypeCourseDurationMap = new HashMap<>();
		Map<CourseSchedule.CourseScheduleType, List<CourseSchedule>> typeCourseMap = courseSchedules.stream().collect(Collectors.groupingBy(CourseSchedule::getType));
		for (Map.Entry<CourseSchedule.CourseScheduleType, List<CourseSchedule>> typeCoursesEntry : typeCourseMap.entrySet()) {
			int totalCourseDuration = 0;
			for (CourseSchedule courseSchedule : typeCoursesEntry.getValue()) {
				//课程时长
				int courseDuration = DateUtil.minutesBetween(courseSchedule.getStartClassTime(), courseSchedule.getEndClassTime());
				totalCourseDuration += courseDuration;
			}
			courseTypeCourseDurationMap.put(typeCoursesEntry.getKey(), totalCourseDuration);
		}

		List<CourseScheduleStudentPayment> courseScheduleStudentPayments = new ArrayList<>();

		List<String> allBatchNos = new ArrayList<>();

		for (Integer studentId : studentIds) {
			for (Map.Entry<CourseSchedule.CourseScheduleType, List<CourseSchedule>> courseScheduleTypeListEntry : typeCourseMap.entrySet()) {
				//当前课程类型总课程时长
				Integer typeCourseDuration = courseTypeCourseDurationMap.get(courseScheduleTypeListEntry.getKey());

				String batchNo = musicGroupPaymentStudentCourseDetailDao.getUnUseBatchNoWithStudentAndCourseTypeAndCourseMinutes(musicGroupId, studentId, courseScheduleTypeListEntry.getKey(),null);

				List<MusicGroupPaymentStudentCourseDetail> musicGroupPaymentStudentCourseDetails = musicGroupPaymentStudentCourseDetailDao.getUnUseWithStudentAndCourseTypeAndCourseMinutes(batchNo, studentId, courseScheduleTypeListEntry.getKey());
				int totalCourseMinutes = musicGroupPaymentStudentCourseDetails.stream().mapToInt(MusicGroupPaymentStudentCourseDetail::getTotalCourseMinutes).reduce(0, Integer::sum);
				if(CollectionUtils.isEmpty(musicGroupPaymentStudentCourseDetails) || totalCourseMinutes < typeCourseDuration){
					SysUser user = teacherDao.getUser(studentId);
					throw new BizException("{}在{}课程类型上的课程时长不足", user.getUsername(), courseScheduleTypeListEntry.getKey().getMsg());
				}

				Set<Long> calenderIds = musicGroupPaymentStudentCourseDetails.stream().map(MusicGroupPaymentStudentCourseDetail::getMusicGroupPaymentCalenderId).collect(Collectors.toSet());

				List<MusicGroupPaymentCalender> musicGroupPaymentCalenders = musicGroupPaymentCalenderDao.queryByIds(calenderIds);
				if(CollectionUtils.isEmpty(musicGroupPaymentCalenders)||musicGroupPaymentCalenders.size()!=calenderIds.size()){
					throw new BizException("缴费设置异常");
				}

				List<MusicGroupPaymentCalenderCourseSettings> musicGroupPaymentCalenderCourseSettings = musicGroupPaymentCalenderCourseSettingsDao.getWithPaymentCalendersAndCourseType(calenderIds, courseScheduleTypeListEntry.getKey());
				int originalCourseTotalMinutes = musicGroupPaymentCalenderCourseSettings.stream().mapToInt(MusicGroupPaymentCalenderCourseSettings::getCourseTotalMinuties).reduce(0, Integer::sum);
				if(Objects.isNull(musicGroupPaymentCalenderCourseSettings)||originalCourseTotalMinutes!=totalCourseMinutes){
					throw new BizException("缴费设置异常");
				}

				allBatchNos.add(batchNo);

				BigDecimal totalCourseOriginalPrice = musicGroupPaymentCalenderCourseSettings.stream().map(MusicGroupPaymentCalenderCourseSettings::getCourseOriginalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
				BigDecimal totalCourseCurrentPrice = musicGroupPaymentCalenderCourseSettings.stream().map(MusicGroupPaymentCalenderCourseSettings::getCourseCurrentPrice).reduce(BigDecimal.ZERO, BigDecimal::add);

				//课程每分钟原价
				BigDecimal unitMinuteOriginalPrice = totalCourseOriginalPrice.divide(new BigDecimal(originalCourseTotalMinutes), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);
				//课程每分钟现价
				BigDecimal unitMinuteCurrentPrice = totalCourseCurrentPrice.divide(new BigDecimal(originalCourseTotalMinutes), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);

				if(MusicGroupPaymentCalender.PayUserType.SCHOOL.equals(musicGroupPaymentCalenders.get(0).getPayUserType())){
					unitMinuteCurrentPrice = new BigDecimal("0");
				}

				List<CourseScheduleStudentPayment> typeCourseStudentPayments = new ArrayList<>();
				courseScheduleTypeListEntry.getValue().sort(Comparator.comparing(CourseSchedule::getStartClassTime));
				BigDecimal typeCourseTotalOriginalPrice = new BigDecimal("0"), typeCourseTotalCurrentPrice = new BigDecimal("0");
				for (CourseSchedule courseSchedule : courseScheduleTypeListEntry.getValue()) {
					//课程时长
					int courseDuration = DateUtil.minutesBetween(courseSchedule.getStartClassTime(), courseSchedule.getEndClassTime());

					//课程原价
					BigDecimal courseOriginalPrice = unitMinuteOriginalPrice.multiply(new BigDecimal(courseDuration)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN);
					typeCourseTotalOriginalPrice = typeCourseTotalOriginalPrice.add(courseOriginalPrice);

					//课程现价
					BigDecimal courseCurrentPrice = unitMinuteCurrentPrice.multiply(new BigDecimal(courseDuration)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN);
					typeCourseTotalCurrentPrice = typeCourseTotalCurrentPrice.add(courseCurrentPrice);

					CourseScheduleStudentPayment cssp = new CourseScheduleStudentPayment();
					cssp.setGroupType(courseSchedule.getGroupType());
					cssp.setMusicGroupId(courseSchedule.getMusicGroupId());
					cssp.setCourseScheduleId(courseSchedule.getId());
					cssp.setClassGroupId(courseSchedule.getClassGroupId());
					cssp.setBatchNo(batchNo);
					cssp.setUserId(studentId);
					cssp.setOriginalPrice(courseOriginalPrice);
					cssp.setExpectPrice(courseCurrentPrice);
					cssp.setActualPrice(BigDecimal.ZERO);
					typeCourseStudentPayments.add(cssp);
				}

				typeCourseStudentPayments.get(0).setOriginalPrice(typeCourseStudentPayments.get(0).getOriginalPrice().add(totalCourseOriginalPrice.subtract(typeCourseTotalOriginalPrice)));
				if(MusicGroupPaymentCalender.PayUserType.STUDENT.equals(musicGroupPaymentCalenders.get(0).getPayUserType())) {
					typeCourseStudentPayments.get(0).setExpectPrice(typeCourseStudentPayments.get(0).getExpectPrice().add(totalCourseCurrentPrice.subtract(typeCourseTotalCurrentPrice)));
				}
				courseScheduleStudentPayments.addAll(typeCourseStudentPayments);
				for (MusicGroupPaymentStudentCourseDetail musicGroupPaymentStudentCourseDetail : musicGroupPaymentStudentCourseDetails) {
					if(typeCourseDuration>musicGroupPaymentStudentCourseDetail.getTotalCourseMinutes()){
						musicGroupPaymentStudentCourseDetail.setUsedCourseMinutes(musicGroupPaymentStudentCourseDetail.getTotalCourseMinutes());
						typeCourseDuration = typeCourseDuration-musicGroupPaymentStudentCourseDetail.getTotalCourseMinutes();
					}else{
						musicGroupPaymentStudentCourseDetail.setUsedCourseMinutes(typeCourseDuration);
						typeCourseDuration = 0;
					}
				}
				musicGroupPaymentStudentCourseDetailDao.batchUpdate(musicGroupPaymentStudentCourseDetails);
			}
		}
		courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);
//		for (String batchNo : allBatchNos) {
//			updateForMusicGroupWithPaymentCalender(batchNo);
//		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
	public void updateForMusicGroupWithPaymentCalender(String batchNo) {
		List<CourseScheduleStudentPayment> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.findByBatchNo(batchNo);
		if(CollectionUtils.isEmpty(courseScheduleStudentPayments)){
			return;
		}
		List<MusicGroupPaymentCalender> musicGroupPaymentCalenders = musicGroupPaymentCalenderDao.findByBatchNo(batchNo);
		if(CollectionUtils.isEmpty(musicGroupPaymentCalenders)){
			return;
		}
		List<Long> calenderIds = musicGroupPaymentCalenders.stream().map(MusicGroupPaymentCalender::getId).collect(Collectors.toList());
		List<MusicGroupPaymentCalenderDetail> calenderDetails = musicGroupPaymentCalenderDetailDao.getWithCalenderIds(calenderIds);

		Map<Integer, List<CourseScheduleStudentPayment>> studentStudentPaymentMap = courseScheduleStudentPayments.stream().collect(Collectors.groupingBy(CourseScheduleStudentPayment::getUserId));
		Map<Integer, List<MusicGroupPaymentCalenderDetail>> studentCalenderDetailMap = calenderDetails.stream().collect(Collectors.groupingBy(MusicGroupPaymentCalenderDetail::getUserId));

		List<CourseScheduleStudentPayment> updateStudentPayments = new ArrayList<>();

		for (Map.Entry<Integer, List<CourseScheduleStudentPayment>> studentStudentPaymentMapEntry : studentStudentPaymentMap.entrySet()) {
			List<MusicGroupPaymentCalenderDetail> musicGroupPaymentCalenderDetails = studentCalenderDetailMap.get(studentStudentPaymentMapEntry.getKey());

			if(CollectionUtils.isEmpty(musicGroupPaymentCalenderDetails)){
				continue;
			}

			//学员总缴费金额
			BigDecimal totalActualAmount = musicGroupPaymentCalenderDetails.stream().map(e -> Objects.isNull(e.getActualAmount())?BigDecimal.ZERO:e.getActualAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);

			for (CourseScheduleStudentPayment courseScheduleStudentPayment : studentStudentPaymentMapEntry.getValue()) {
				if(totalActualAmount.compareTo(courseScheduleStudentPayment.getExpectPrice())>=0){
					courseScheduleStudentPayment.setActualPrice(courseScheduleStudentPayment.getExpectPrice());
					totalActualAmount=totalActualAmount.subtract(courseScheduleStudentPayment.getExpectPrice());
				}else{
					courseScheduleStudentPayment.setActualPrice(totalActualAmount);
					totalActualAmount=BigDecimal.ZERO;
				}
				updateStudentPayments.add(courseScheduleStudentPayment);
			}
		}

		courseScheduleStudentPaymentDao.batchUpdate(updateStudentPayments);
	}

	@Override
	public List<CourseScheduleStudentPayment> getClassGroupNoStartCourse(List<Integer> classGroupIds) {
		List<CourseScheduleStudentPayment> noStartCourses = courseScheduleStudentPaymentDao.getClassGroupNoStartCourse(classGroupIds);
		Set<Long> needCoursesIds = new HashSet<>();
		List<CourseScheduleStudentPayment> needCourses = new ArrayList<>();
		for (CourseScheduleStudentPayment noStartCourse : noStartCourses) {
			if (needCoursesIds.contains(noStartCourse.getCourseScheduleId())) continue;
			needCoursesIds.add(noStartCourse.getId());
			needCourses.add(noStartCourse);
		}
		return needCourses;
	}

	@Override
	public int deleteByMusicGroupId(String musicGroupId, GroupType music) {
		return courseScheduleStudentPaymentDao.deleteByMusicGroupId(musicGroupId,music);
	}


	@Override
	public Map<String, Object> queryCourseStudentList(CourseScheduleStudentPaymentQueryInfo queryInfo) {
		PageInfo<CourseScheduleStudentListDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
		Map<String, Object> params = new HashMap<String, Object>();
		MapUtil.populateMap(params, queryInfo);
		List<StudentAttendance> studentAttendances = studentAttendanceDao.findByCourseId(queryInfo.getCourseScheduleId().longValue());

		List<CourseScheduleStudentListDto> dataList = new ArrayList<>();
		int count = courseScheduleStudentPaymentDao.countAll(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			List<CourseScheduleStudentPayment> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.queryAll(params);
			Set<Integer> studentIds = courseScheduleStudentPayments.stream().map(CourseScheduleStudentPayment::getUserId).collect(Collectors.toSet());
			List<SimpleUserDto> usersSimpleInfo = teacherDao.getUsersSimpleInfo(new ArrayList<>(studentIds));
			List<Student> students = studentDao.findByStudentIds(new ArrayList<>(studentIds));
			Set<Integer> subjectIds = new HashSet<>();
			for (Student student : students) {
				if(StringUtils.isBlank(student.getSubjectIdList())){
					continue;
				}
				Set<Integer> studentSubjectIds = Arrays.stream(student.getSubjectIdList().split(",")).mapToInt(Integer::valueOf).boxed().collect(Collectors.toSet());
				subjectIds.addAll(studentSubjectIds);
			}
			List<Subject> subjects = new ArrayList<>();
			if(!CollectionUtils.isEmpty(subjectIds)){
				subjects = subjectDao.findBySubjectIds(new ArrayList<>(subjectIds));
			}
			Map<Integer, StudentAttendance> studentAttendanceMap = studentAttendances.stream().collect(Collectors.toMap(StudentAttendance::getUserId, s -> s, (s1, s2) -> s1));
			Map<Integer, SimpleUserDto> studentInfoMap = usersSimpleInfo.stream().collect(Collectors.toMap(SimpleUserDto::getUserId, s -> s, (s1, s2) -> s1));
			Map<Integer, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getUserId, s -> s, (s1, s2) -> s1));
			for (CourseScheduleStudentPayment courseScheduleStudentPayment : courseScheduleStudentPayments) {
				CourseScheduleStudentListDto cssld=new CourseScheduleStudentListDto();
				cssld.setStudentId(courseScheduleStudentPayment.getUserId());
				cssld.setStudentName(studentInfoMap.containsKey(cssld.getStudentId())?studentInfoMap.get(cssld.getStudentId()).getNickName():"");
				cssld.setPhone(studentInfoMap.containsKey(cssld.getStudentId())?studentInfoMap.get(cssld.getStudentId()).getPhone():"");
				cssld.setSubjectIds(studentMap.containsKey(cssld.getStudentId())?studentMap.get(cssld.getStudentId()).getSubjectIdList():"");
				if(StringUtils.isNotBlank(cssld.getSubjectIds())){
					Set<Integer> studentSubjectIds = Arrays.stream(cssld.getSubjectIds().split(",")).mapToInt(Integer::valueOf).boxed().collect(Collectors.toSet());
					List<String> subjectNames = subjects.stream().filter(s -> studentSubjectIds.contains(s.getId())).map(Subject::getName).collect(Collectors.toList());
					cssld.setSubjectNames(StringUtils.join(subjectNames, ","));
				}
				if(studentAttendanceMap.containsKey(cssld.getStudentId())){
					StudentAttendance studentAttendance = studentAttendanceMap.get(cssld.getStudentId());
					cssld.setSignInTime(studentAttendance.getSignInTime());
					cssld.setSignOutTime(studentAttendance.getSignOutTime());
					cssld.setStatus(studentAttendance.getStatus());
				}
				dataList.add(cssld);
			}
		}
		pageInfo.setRows(dataList);

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

		if(queryInfo.getPage()==1){
			Map<String, Integer> countInfo = new HashMap<>();
			countInfo.put("totalStudentNum", count);
			countInfo.put("signInStudentNum", (int) studentAttendances.stream().filter(s -> Objects.nonNull(s.getSignInTime())).count());
			countInfo.put("leaveStudentNum", (int) studentAttendances.stream().filter(s -> StudentAttendanceStatusEnum.LEAVE.equals(s.getStatus())).count());
			countInfo.put("truantStudentNum", (int) studentAttendances.stream().filter(s -> Objects.isNull(s.getStatus())||StudentAttendanceStatusEnum.TRUANT.equals(s.getStatus())).count());

			result.put("countInfo", countInfo);
		}

		return result;
	}

	@Override
	public List<SimpleUserDto> getCourseStudents(List<Long> courseIds) {
		if(CollectionUtils.isEmpty(courseIds)){
			return Collections.EMPTY_LIST;
		}
		List<CourseScheduleStudentPayment> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.findByCourseScheduleIds(courseIds);
		Set<Integer> studentIds = courseScheduleStudentPayments.stream().map(CourseScheduleStudentPayment::getUserId).collect(Collectors.toSet());
		List<SimpleUserDto> usersSimpleInfo = teacherDao.getUsersSimpleInfo(new ArrayList<>(studentIds));
		List<Student> students = studentDao.findByStudentIds(new ArrayList<>(studentIds));
		Map<Integer, Student> idStudentMap = students.stream().collect(Collectors.toMap(Student::getUserId, s -> s, (s1, s2) -> s1));
		Set<Integer> subjectIds = new HashSet<>();
		for (Student student : students) {
			if(StringUtils.isBlank(student.getSubjectIdList())){
				continue;
			}
			subjectIds.addAll(Arrays.stream(student.getSubjectIdList().split(",")).map(Integer::valueOf).collect(Collectors.toSet()));
		}
		if(!CollectionUtils.isEmpty(subjectIds)){
			List<Subject> subjects = subjectDao.findBySubjectIds(new ArrayList<>(subjectIds));
			for (SimpleUserDto simpleUserDto : usersSimpleInfo) {
				if(StringUtils.isBlank(idStudentMap.get(simpleUserDto.getUserId()).getSubjectIdList())){
					continue;
				}
				Set<Integer> userSubjectIds = Arrays.stream(idStudentMap.get(simpleUserDto.getUserId()).getSubjectIdList().split(",")).map(Integer::valueOf).collect(Collectors.toSet());
				simpleUserDto.setSubjectNames(subjects.stream().filter(s->userSubjectIds.contains(s.getId())).map(Subject::getName).collect(Collectors.joining(",")));
			}
		}
		return usersSimpleInfo;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateCourseActualPrice(List<String> groupIds, List<Integer> userIds, GroupType groupType) {
		studentDao.lockUser(1);
		List<CourseScheduleStudentPaymentDto> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.findNeedUpdateActualPriceStudentCourses(groupIds, userIds, groupType);
		if(CollectionUtils.isEmpty(courseScheduleStudentPayments)){
			return;
		}
		List<CourseScheduleStudentPayment> needUpdates = new ArrayList<>();
		Map<CourseStatusEnum, List<CourseScheduleStudentPaymentDto>> statusCoursesMap = courseScheduleStudentPayments.stream().collect(Collectors.groupingBy(CourseScheduleStudentPaymentDto::getCourseStatus));
		if(statusCoursesMap.containsKey(CourseStatusEnum.NOT_START)){
			List<CourseScheduleStudentPaymentDto> notStartCourses = statusCoursesMap.get(CourseStatusEnum.NOT_START);
			for (CourseScheduleStudentPaymentDto notStartCourse : notStartCourses) {
				studentRegistrationService.updateUserSurplusCourseFee(notStartCourse.getUserId(), notStartCourse.getMusicGroupId(), notStartCourse.getActualPrice(), StringUtils.join("课程状态变更到未开始，退还缴费：", notStartCourse.getCourseScheduleId()), null);
				notStartCourse.setActualPrice(BigDecimal.ZERO);
				needUpdates.add(notStartCourse);
			}
		}

		if(statusCoursesMap.containsKey(CourseStatusEnum.OVER)){
			List<CourseScheduleStudentPaymentDto> notStartCourses = statusCoursesMap.get(CourseStatusEnum.OVER);
			notStartCourses.sort(Comparator.comparing(CourseScheduleStudentPaymentDto::getCourseStartTime));
			for (CourseScheduleStudentPaymentDto notStartCourse : notStartCourses) {
				BigDecimal actualPrice = Objects.isNull(notStartCourse.getActualPrice())?BigDecimal.ZERO:notStartCourse.getActualPrice();
				BigDecimal deductAmount = studentRegistrationService.updateUserSurplusCourseFee(notStartCourse.getUserId(), notStartCourse.getMusicGroupId(), (notStartCourse.getExpectPrice().subtract(actualPrice)).negate(), StringUtils.join("课程结束，扣除课程费用：", notStartCourse.getCourseScheduleId()), null);
				if(actualPrice.compareTo(actualPrice.add(deductAmount.abs()))==0){
					continue;
				}
				notStartCourse.setActualPrice(actualPrice.add(deductAmount.abs()));
				needUpdates.add(notStartCourse);
			}
		}
		if(!CollectionUtils.isEmpty(needUpdates)){
			courseScheduleStudentPaymentDao.batchUpdate(needUpdates);
		}
	}
}
