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

import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.CourseScheduleComplaintsDao;
import com.ym.mec.biz.dal.dao.CourseScheduleDao;
import com.ym.mec.biz.dal.dao.StudentAttendanceDao;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dto.ClassDateAdjustDto;
import com.ym.mec.biz.dal.dto.CourseScheduleDto;
import com.ym.mec.biz.dal.dto.TeacherAttendanceDto;
import com.ym.mec.biz.dal.entity.ClassGroupTeacherMapper;
import com.ym.mec.biz.dal.entity.CourseSchedule;
import com.ym.mec.biz.dal.entity.CourseScheduleComplaints;
import com.ym.mec.biz.dal.entity.SysConfig;
import com.ym.mec.biz.dal.enums.AuditStatusEnum;
import com.ym.mec.biz.dal.enums.CourseStatusEnum;
import com.ym.mec.biz.dal.enums.ParamEnum;
import com.ym.mec.biz.dal.enums.YesOrNoEnum;
import com.ym.mec.biz.dal.page.CourseScheduleQueryInfo;
import com.ym.mec.biz.dal.page.StudentCourseScheduleRecordQueryInfo;
import com.ym.mec.biz.service.CourseScheduleService;
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.service.impl.BaseServiceImpl;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class CourseScheduleServiceImpl extends BaseServiceImpl<Long, CourseSchedule>  implements CourseScheduleService {
	
	@Autowired
	private CourseScheduleDao courseScheduleDao;
	@Autowired
	private SysUserFeignService sysUserFeignService;
	@Autowired
	private CourseScheduleComplaintsDao courseScheduleComplaintsDao;
	@Autowired
	private SysConfigDao sysConfigDao;
	@Autowired
	private StudentAttendanceDao studentAttendanceDao;

	@Override
	public BaseDAO<Long, CourseSchedule> getDAO() {
		return courseScheduleDao;
	}

	@Override
	public TeacherAttendanceDto getCurrentCourseDetail(Long courseID) {
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("获取用户信息失败");
		}
		if(Objects.isNull(courseID)){
			throw new BizException("参数错误!");
		}
		TeacherAttendanceDto currentCourseDetail = courseScheduleDao.getCurrentCourseDetail(courseID);
		if(Objects.isNull(currentCourseDetail)){
		    throw new BizException("课程不存在");
        }

		currentCourseDetail.setOnlyNormal(enableOnlyNormalAttendance(currentCourseDetail, user.getId().longValue()));

		currentCourseDetail.setCurrentTime(new Date());
		SysConfig advance_sign_in_minutes = sysConfigDao.findByParamName("advance_sign_in_minutes");
		currentCourseDetail.setAdvanceSignInMinutes(Integer.parseInt(advance_sign_in_minutes.getParanValue()));
		SysConfig attendance_range = sysConfigDao.findByParamName("attendance_range");
		currentCourseDetail.setAttendanceRange(Integer.parseInt(attendance_range.getParanValue()));
		return currentCourseDetail;
	}

	@Override
	public YesOrNoEnum enableOnlyNormalAttendance(TeacherAttendanceDto teacherAttendanceDto,Long userId){
		Date endTime=teacherAttendanceDto.getStartClassTime();
		Date startTime=DateUtil.addHours(endTime,-1);
		TeacherAttendanceDto teacherCourseByDateSpeed = courseScheduleDao.getTeacherCourseByDateSpeed(startTime, endTime, userId);
		if(Objects.isNull(teacherCourseByDateSpeed)){
			return YesOrNoEnum.NO;
		}else{
			int i = studentAttendanceDao.countByCourseSchedule(teacherCourseByDateSpeed.getCourseScheduleId());
			if(i>0&&teacherAttendanceDto.getSchoolId().equals(teacherCourseByDateSpeed.getSchoolId())){
				return YesOrNoEnum.YES;
			}else{
				return YesOrNoEnum.NO;
			}
		}
	}

	@Override
	public void batchAddCourseSchedule(List<CourseSchedule> courseSchedules) {
		if(Objects.isNull(courseSchedules)&&courseSchedules.size()<=0){
			throw new BizException("参数错误!");
		}
		checkCourseSchedule(courseSchedules);
        createCourseScheduleName(courseSchedules);
		courseScheduleDao.batchAddCourseSchedules(courseSchedules);
	}

	@Override
	public void batchUpdateCourseSchedule(List<CourseSchedule> courseSchedules, Long musicGroupID) {
		if(Objects.isNull(courseSchedules)&&courseSchedules.size()<=0){
			throw new BizException("请选择需要调整的课程!");
		}
		if(Objects.isNull(musicGroupID)){
			throw new BizException("请指定乐团!");
		}
		checkCourseSchedule(courseSchedules);
        courseScheduleDao.deleteCourseSchedulesByMusicGroupID(musicGroupID);
        courseScheduleDao.batchAddCourseSchedules(courseSchedules);
	}

	@Override
	public List<CourseScheduleDto> getCourseSchedulesWithDate(Date classDate) {
		if(Objects.isNull(classDate)){
			throw new BizException("请选择日期!");
		}
		return courseScheduleDao.getCourseSchedulesWithDate(classDate);
	}

	@Override
	public List<Date> getCourseScheduleDateByMonth(Long musicGroupID, Date month) {
		if(Objects.isNull(month)||Objects.isNull(musicGroupID)){
			throw new BizException("请选择日期并指定乐团!");
		}
		return courseScheduleDao.getCourseScheduleDateByMonth(musicGroupID,month);
	}

	@Override
	public List<Date> getCourseScheduleDateByStudent(Date month, Integer isAttend) {
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("获取用户信息失败");
		}
		return courseScheduleDao.getStudentCourseScheduleDate(user.getId().longValue(),month,isAttend);
	}

	@Override
	public List<Date> getCourseScheduleDateByTeacher(Date month) {
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("获取用户信息失败");
		}
		return courseScheduleDao.getTeacherCourseScheduleDate(user.getId().longValue(),month);
	}

	@Override
	public List<Date> getCourseScheduleDates(Date month) {
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("请登录");
		}
		return courseScheduleDao.getCourseScheduleDate(month,user.getOrganId().longValue());
	}

	@Override
	public Map getStudentCourseSchedulesWithDate(Date classDate) {
		if(Objects.isNull(classDate)){
			throw new BizException("请选择上课日期!");
		}

		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("请登录");
		}
		SysConfig vip_appeal_days_range = sysConfigDao.findByParamName("vip_appeal_days_range");
		Map<String,Object> result=new HashMap<>();
		result.put("vipAppealDaysRange",vip_appeal_days_range.getParanValue());
		result.put("list",courseScheduleDao.getStudentCourseSchedulesWithDate(classDate,user.getId().longValue()));
		return result;
	}

	@Override
	public PageInfo getCourseSchedulesWithDateByEdu(CourseScheduleQueryInfo queryInfo) {
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("请登录");
		}
		queryInfo.setOrganId(user.getOrganId().longValue());
		PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());

		if(Objects.isNull(queryInfo.getDate())){
			queryInfo.setDate(new Date());
		}

		Map<String, Object> params = new HashMap<>();
		MapUtil.populateMap(params, queryInfo);

		List dataList = null;
		int count = courseScheduleDao.countCourseSchedulesWithDate(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = courseScheduleDao.getCourseSchedulesWithDate(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
	}

	@Override
	public List<CourseScheduleDto> getTeacherCourseSchedulesWithDate(Date classDate) {
		if(Objects.isNull(classDate)){
			throw new BizException("请选择上课日期!");
		}
		SysUser user = sysUserFeignService.queryUserInfo();
		if(null==user){
			throw new BizException("请登录");
		}
		return courseScheduleDao.getTeacherCourseSchedulesWithDate(classDate,user.getId().longValue());
	}

	/**
	 * 判断课程安排中是否存在冲突
	 * 计算课程课次
	 * @param courseSchedules
	 * @return
	 */
	private void checkCourseSchedule(List<CourseSchedule> courseSchedules){
		List<Date> classDates = courseSchedules.stream().map(CourseSchedule::getClassDate).collect(Collectors.toList());
		List<CourseSchedule> byClassGroupAndDate = courseScheduleDao.findByClassGroupAndDate(classDates, null);
		List<CourseSchedule> collect3 = Stream.of(courseSchedules, byClassGroupAndDate).flatMap(Collection::stream).collect(Collectors.toList());
		Map<Date, List<CourseSchedule>> collect = collect3.stream().collect(Collectors.groupingBy(CourseSchedule::getClassDate));
		for(Date keyDate:collect.keySet()){
			List<CourseSchedule> currentCourses=collect.get(keyDate);
			Map<Date, List<CourseSchedule>> collect1 = currentCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getStartClassTime));
			if(currentCourses.size()==1&&currentCourses.size()==collect1.size()){
				continue;
			}else if(currentCourses.size()>1&&currentCourses.size()==collect1.size()){
				ArrayList<CourseSchedule> collect2 = currentCourses
						.stream()
						.collect(Collectors.collectingAndThen(Collectors.toCollection(()
								-> new TreeSet<CourseSchedule>(Comparator.comparing(o -> o.getStartClassTime() + "-" + o.getEndClassTime()))), ArrayList::new));
				if(collect2.size()!=currentCourses.size()){
					throw new BizException("课程安排上存在在同一时间开课的课程");
				}
			}else{
				for(Date keyDate2:collect1.keySet()){
					List<CourseSchedule> currentCourses1=collect1.get(keyDate2);
					if(currentCourses1.size()>ParamEnum.ONE.getCode()){
						Map<Long, List<CourseSchedule>> collect2 = currentCourses1.stream().collect(
								Collectors.groupingBy(CourseSchedule::getTeacherId));
						for (Long teacherID:collect2.keySet()){
							if(collect2.get(teacherID).size()>ParamEnum.ONE.getCode()){
								throw new BizException(new SimpleDateFormat("yyyy-MM-dd").format(keyDate)+"日的课程安排在教师分配上有冲突!");
							}
						}
						List<Integer> repeatClassGroupIds = currentCourses1.stream().map(CourseSchedule::getClassGroupId).collect(Collectors.toList());
						Integer[] counts=courseScheduleDao.countStudentInClass(repeatClassGroupIds);
						for (Integer i:counts){
							if (i>ParamEnum.ONE.getCode()){
								throw new BizException(new SimpleDateFormat("yyyy-MM-dd").format(keyDate)+"日的课程安排有学生重复!");
							}
						}
					}
				}
			}
		}
	}

	/**
	 * @Author: Joburgess
	 * @Date: 2019/9/29
	 * 生成课程名
	 */
	private void createCourseScheduleName(List<CourseSchedule> courseSchedules){
        List<Integer> classGroupIds = courseSchedules.stream().map(CourseSchedule::getClassGroupId).collect(Collectors.toList());
        HashSet<Integer> temp=new HashSet<>(classGroupIds);
        classGroupIds=new ArrayList<>(temp);
        Map<Integer,String> classIdSubjectNameMap=convertToMap(courseScheduleDao.findClassSubjects(classGroupIds));
        courseSchedules.forEach(courseSchedule -> {
            StringBuilder className=new StringBuilder(classIdSubjectNameMap.get(courseSchedule.getClassGroupId()));
            className.append("-");
            className.append(courseSchedule.getType().getMsg());
            courseSchedule.setName(className.toString());
        });
    }

	private Map<Integer,String> convertToMap(String[] result){
		Map<Integer,String> map=new HashMap<>();
		for(String s:result){
			map.put(Integer.valueOf(s.split("-")[0]),s.split("-")[1].replace(",","/"));
		}
		return map;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void classStartDateAdjust(ClassDateAdjustDto classDateAdjustDto) {

		CourseSchedule nowCourseSchedule = courseScheduleDao.get(classDateAdjustDto.getId());
		if(DateUtils.isSameDay(nowCourseSchedule.getClassDate(),classDateAdjustDto.getClassDate())&&
				DateUtils.isSameDay(nowCourseSchedule.getStartClassTime(),classDateAdjustDto.getStartClassTime())&&
				DateUtils.isSameDay(nowCourseSchedule.getEndClassTime(),classDateAdjustDto.getEndClassTime())){
			courseScheduleDao.update(classDateAdjustDto);
			return;
		}

		//单次调整课时
		if(null==classDateAdjustDto.getAdjustStartTime()&&null==classDateAdjustDto.getAdjustEndTime()){
			ArrayList<Date> dates = new ArrayList<>();
			dates.add(classDateAdjustDto.getClassDate());
			List<CourseSchedule> coursesByDates = courseScheduleDao.findOverLapCoursesInDay(dates,classDateAdjustDto.getStartClassTime(),classDateAdjustDto.getEndClassTime());
			//判断是否存在同时间上课的班级
			if(null==coursesByDates||coursesByDates.size()<=0){
				courseScheduleDao.update(classDateAdjustDto);
			}else{
				coursesByDates.forEach(courseSchedule -> {
					if(courseSchedule.getTeacherId().equals(classDateAdjustDto.getTeacherId())){
						//存在时间重叠，需要前端重新调整
						throw new BizException("所选教师在当天课程安排上存在重叠!");
					}
				});
				List<Integer> repeatClassGroupIds = coursesByDates.stream().map(CourseSchedule::getClassGroupId).collect(Collectors.toList());
				checkStudentIsRepeat(repeatClassGroupIds);
				courseScheduleDao.update(classDateAdjustDto);
			}
		}else{
			//根据时间段调整课时
			List<Date> dates= DateUtil.getWeekDays(classDateAdjustDto.getAdjustStartTime(),classDateAdjustDto.getAdjustEndTime(),classDateAdjustDto.getWeekNum());
			List<CourseSchedule> coursesByDates = courseScheduleDao.findOverLapCoursesInDay(dates,classDateAdjustDto.getStartClassTime(),classDateAdjustDto.getEndClassTime());
			//判断是否存在同时间上课的班级
			if(null==coursesByDates||coursesByDates.size()<=0){
				createNewCourseByAdjust(dates,classDateAdjustDto);
			}else{
				//判断教师是否在时间安排上存在冲突
				SimpleDateFormat s1=new SimpleDateFormat("yyyy-MM-dd"),
						s2=new SimpleDateFormat("HH:mm:ss"),
						s3=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				List<CourseSchedule> teacherCoursesInDates = courseScheduleDao.findTeacherCoursesInDates(dates, classDateAdjustDto.getTeacherId());
				teacherCoursesInDates.forEach(courseSchedule -> {
					Date updateStartClassTime = null,updateEndClassTime=null;
					try {
						updateStartClassTime=s3.parse(s1.format(courseSchedule.getClassDate())+" "+s2.format(classDateAdjustDto.getStartClassTime()));
						updateEndClassTime=s3.parse(s1.format(courseSchedule.getClassDate())+" "+s2.format(classDateAdjustDto.getEndClassTime()));
					} catch (ParseException e) {
						e.printStackTrace();
					}
					boolean isDuplication=(courseSchedule.getStartClassTime().after(updateStartClassTime)
							&&courseSchedule.getStartClassTime().before(updateEndClassTime))
							||(updateStartClassTime.after(courseSchedule.getStartClassTime())
							&&updateStartClassTime.before(courseSchedule.getEndClassTime()));
					if(isDuplication){
						throw new BizException("在所选时段内教师上课时间存在重复！");
					}

				});
				//判断学生在时间安排上是否存在冲突
				Map<Date, List<CourseSchedule>> collect = coursesByDates.stream().collect(Collectors.groupingBy(CourseSchedule::getClassDate));
				for(Date keyDate:collect.keySet()){
					List<Integer> repeatClassGroupIds = collect.get(keyDate).stream().map(CourseSchedule::getClassGroupId).collect(Collectors.toList());
					checkStudentIsRepeat(repeatClassGroupIds);
				}
				createNewCourseByAdjust(dates,classDateAdjustDto);
			}
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void courseSwap(Long courseScheduleId1, Long courseScheduleId2) {
		List<CourseSchedule> courseSchedules = courseScheduleDao.findCourseSchedulesByIds(new Long[]{courseScheduleId1, courseScheduleId2});
		CourseSchedule temp=new CourseSchedule();
		BeanUtils.copyProperties(courseSchedules.get(0),temp);
		courseSchedules.get(0).setClassDate(courseSchedules.get(1).getClassDate());
		courseSchedules.get(0).setStartClassTime(courseSchedules.get(1).getStartClassTime());
		courseSchedules.get(0).setEndClassTime(courseSchedules.get(1).getEndClassTime());
		courseSchedules.get(1).setClassDate(temp.getClassDate());
		courseSchedules.get(1).setStartClassTime(temp.getStartClassTime());
		courseSchedules.get(1).setEndClassTime(temp.getEndClassTime());
		courseScheduleDao.update(courseSchedules.get(0));
		courseScheduleDao.update(courseSchedules.get(1));
	}

	@Override
	public void courseSwapWithDay(Date day1, Date day2) {

	}

	@Override
    public List<CourseSchedule> findClassGroupNoStartCourseSchedules(List<ClassGroupTeacherMapper> classGroupTeacherMapperList) {
		return courseScheduleDao.findClassGroupNoStartCourseSchedules(classGroupTeacherMapperList);
    }

    /**
	 * @Author: Joburgess
	 * @Date: 2019/9/24
	 * 检测学生是否存在重复
	 */
	private void checkStudentIsRepeat(List<Integer> studentIds){
		Integer[] counts=courseScheduleDao.countStudentInClass(studentIds);
		for (Integer i:counts){
			if (i>ParamEnum.ONE.getCode()){
				throw new BizException("所选课程安排日期有学生重复!");
			}
		}
	}

	/**
	 * @Author: Joburgess
	 * @Date: 2019/9/24
	 * 根据课时调整信息增加新课程
	 */
	private void createNewCourseByAdjust(List<Date> dates,ClassDateAdjustDto classDateAdjustDto){
		CourseSchedule schedule=courseScheduleDao.get(classDateAdjustDto.getId());
		List<CourseSchedule> courseSchedules=new ArrayList<>();
		courseScheduleDao.delete(classDateAdjustDto.getId());
		for (Date date:dates){
			CourseSchedule courseSchedule=new CourseSchedule();
			BeanUtils.copyProperties(schedule,courseSchedule);
			courseSchedule.setClassDate(date);
			courseSchedule.setStartClassTime(classDateAdjustDto.getStartClassTime());
			courseSchedule.setEndClassTime(classDateAdjustDto.getEndClassTime());
			courseSchedule.setTeacherId(classDateAdjustDto.getTeacherId());
			courseSchedules.add(courseSchedule);
		}
		courseScheduleDao.batchAddCourseSchedules(courseSchedules);
	}

	@Override
	public Object findStudentCourseScheduleRecords(StudentCourseScheduleRecordQueryInfo queryInfo) {
		SysUser user = sysUserFeignService.queryUserInfo();
		queryInfo.setUserId(user.getId().longValue());
		PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());

		if(queryInfo.getIsAttend()== YesOrNoEnum.YES){
			pageInfo.setRows(courseScheduleDao.findStudentCourseScheduleNotStartRecords(user.getId().longValue(),
					queryInfo.getDate()));
			return pageInfo;
		}

		Map<String, Object> params = new HashMap<String, Object>();
		MapUtil.populateMap(params, queryInfo);

		List dataList = null;
		int count = courseScheduleDao.countStudentCourseScheduleRecords(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = courseScheduleDao.findStudentCourseScheduleRecords(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
	}

	@Override
	public void courseScheduleCommplaint(CourseScheduleComplaints courseScheduleComplaints) {
		SysUser user = sysUserFeignService.queryUserInfo();

		if(Objects.isNull(user)){
			throw new BizException("请登录");
		}

		if(Objects.isNull(courseScheduleComplaints.getCourseScheduleId())){
			throw new BizException("请指定需要投诉的课程");
		}

		if(StringUtils.isBlank(courseScheduleComplaints.getReason())){
			throw new BizException("请说明投诉原因");
		}

		if(StringUtils.length(courseScheduleComplaints.getReason())>150){
			throw new BizException("字符长度超限");
		}

		courseScheduleComplaints.setUserId(user.getId());

		CourseScheduleComplaints byUserAndCourse = courseScheduleComplaintsDao.findByUserAndCourse(courseScheduleComplaints.getUserId().longValue(), courseScheduleComplaints.getCourseScheduleId());
		if(byUserAndCourse!=null){
			throw new BizException("您已经对该课程进行过申诉操作!");
		}

		CourseScheduleDto courseSchedules = courseScheduleDao.getCourseSchedules(courseScheduleComplaints.getCourseScheduleId());
		Date now=new Date();
		int i = DateUtil.daysBetween(courseSchedules.getEndClassTime(), now);
		SysConfig vip_appeal_days_range = sysConfigDao.findByParamName("vip_appeal_days_range");
		if(i>Integer.parseInt(vip_appeal_days_range.getParanValue())){
			throw new BizException("该课程已超过可申诉时间范围!");
		}
		courseScheduleComplaints.setUserId(user.getId());
		courseScheduleComplaints.setStatus(AuditStatusEnum.ING);
		courseScheduleComplaintsDao.insert(courseScheduleComplaints);
	}

	@Override
	public void courseScheduleCommplaintAudit(CourseScheduleComplaints courseScheduleComplaints) {
		if(Objects.isNull(courseScheduleComplaints.getId())){
			throw new BizException("请指定投诉记录!");
		}
		CourseScheduleComplaints newCourseScheduleComplaints=new CourseScheduleComplaints();
		newCourseScheduleComplaints.setId(courseScheduleComplaints.getId());
		newCourseScheduleComplaints.setStatus(courseScheduleComplaints.getStatus());
		courseScheduleComplaintsDao.update(newCourseScheduleComplaints);
	}

	@Override
	public boolean updateCourseScheduleToOverStatus() {
		List<CourseSchedule> list = courseScheduleDao.queryFinishedWithNoUpdateStatus();
		
		List<CourseSchedule> updateList = new ArrayList<CourseSchedule>();
		Date date = new Date();
		for(CourseSchedule courseSchedule : list){
			courseSchedule.setStatus(CourseStatusEnum.OVER);
			courseSchedule.setUpdateTime(date);
			updateList.add(courseSchedule);
		}
		
		if(updateList.size()>0){
			
		}
		return true;
	}
}