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

import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.StudentServeCourseDto;
import com.ym.mec.biz.dal.dto.StudentServeCourseHomeworkDto;
import com.ym.mec.biz.dal.dto.StudentServeDto;
import com.ym.mec.biz.dal.dto.StudentServiceHomeworkDto;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.GroupType;
import com.ym.mec.biz.dal.enums.TeachModeEnum;
import com.ym.mec.biz.dal.enums.YesOrNoEnum;
import com.ym.mec.biz.service.StudentServeService;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.util.date.DateUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author Joburgess
 * @Date 2020.05.27
 */
@Service
public class StudentServeServiceImpl implements StudentServeService {

    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private StudentCourseHomeworkDao studentCourseHomeworkDao;
    @Autowired
    private ExtracurricularExercisesReplyDao extracurricularExercisesReplyDao;
    @Autowired
    private StudentExtracurricularExercisesSituationDao studentExtracurricularExercisesSituationDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exercisesSituationStatistics(String monday) {
        LocalDate nowDate = LocalDateTime.now(DateUtil.zoneId).toLocalDate();

        int lastWeekTodayUpdateNum = studentExtracurricularExercisesSituationDao.findLastWeekTodayUpdateNum(nowDate.plusDays(-nowDate.getDayOfWeek().getValue()).toString());
        if(lastWeekTodayUpdateNum<=0){
            nowDate = nowDate.plusDays(-nowDate.getDayOfWeek().getValue());
        }

        if(StringUtils.isNotBlank(monday)){
            nowDate=LocalDate.parse(monday, DateUtil.dateFormatter);
        }

        LocalDate monDayDate = nowDate.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
        LocalDate sunDayDate = nowDate.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.SUNDAY.getValue());
        Date nextMonday = Date.from(sunDayDate.plusDays(1).atStartOfDay(DateUtil.zoneId).toInstant());

        List<Integer> beServeStudentIds=studentDao.getServeStudentIds();
        List<Integer> serveStudentIds=new ArrayList<>();
        List<StudentServeCourseDto> serveStudentCourseStartTimes = studentDao.getServeStudentCourseStartTimes(monDayDate.toString());
        Map<Integer, List<StudentServeCourseDto>> studentTimesMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(serveStudentCourseStartTimes)){
            studentTimesMap = serveStudentCourseStartTimes.stream().collect(Collectors.groupingBy(StudentServeCourseDto::getStudentId));
        }
        for (Integer beServeStudentId : beServeStudentIds) {
            List<StudentServeCourseDto> studentServeCourseDtos = studentTimesMap.get(beServeStudentId);
            if(CollectionUtils.isEmpty(studentServeCourseDtos)
                    ||Objects.isNull(studentServeCourseDtos.get(0).getServiceTagUpdateTime())){
                serveStudentIds.add(beServeStudentId);
                continue;
            }
            long thisWeekCourses = studentServeCourseDtos.stream().filter(e -> !e.getCourseStartTime().before(e.getServiceTagUpdateTime()) && e.getCourseStartTime().compareTo(nextMonday) <= 0).count();
            if(thisWeekCourses>0){
                serveStudentIds.add(beServeStudentId);
            }
        }

        BigDecimal currentPage=BigDecimal.ONE,
                pageSize=new BigDecimal(1000),
                total=new BigDecimal(serveStudentIds.size()),
                totalPage=total.divide(pageSize, BigDecimal.ROUND_UP);

        List<StudentExtracurricularExercisesSituation> results=new ArrayList<>();

        while (currentPage.compareTo(totalPage)<=0){
            List<Integer> studentIds=serveStudentIds.stream().skip(pageSize.multiply(currentPage.subtract(BigDecimal.ONE)).longValue()).limit(pageSize.longValue()).collect(Collectors.toList());

            List<StudentServeDto> serviceStudents;
            if(sunDayDate.isBefore(LocalDate.now())){
                serviceStudents = studentDao.getBeServiceStudentsInfoWithStudentsForHistory(monDayDate.toString(), nextMonday,studentIds);
            }else{
                serviceStudents = studentDao.getBeServiceStudentsInfoWithStudents(monDayDate.toString(), new Date(),studentIds);
            }
            if(CollectionUtils.isEmpty(serviceStudents)){
                return;
            }

            Map<Integer, List<StudentServeDto>> studentCoursesMap = serviceStudents.stream().collect(Collectors.groupingBy(StudentServeDto::getUserId));

            //本周不服务学员列表
            Set<Integer> dontServeInCurrentWeekStudentIds=new HashSet<>();
            //本周有课学员编号集合
            Set<Integer> haveClassStudentIds=new HashSet<>();
            //本周无课学员编号集合
            Set<Integer> noClassStudentIds=new HashSet<>();
            for (Map.Entry<Integer, List<StudentServeDto>> studentCoursesEntry : studentCoursesMap.entrySet()) {

                List<StudentServeDto> haveCourseStudents = studentCoursesEntry.getValue().stream().filter(e -> Objects.nonNull(e.getCourseStartTime())).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(haveCourseStudents)){
                    noClassStudentIds.add(studentCoursesEntry.getKey());
                    continue;
                }
                StudentServeDto studentServeDto = haveCourseStudents.stream().min(Comparator.comparing(StudentServeDto::getCourseStartTime)).get();
                if(Objects.isNull(studentServeDto.getCourseStartTime())){
                    noClassStudentIds.add(studentCoursesEntry.getKey());
                    continue;
                }
                if(!studentServeDto.getCourseStartTime().before(nextMonday)){
                    dontServeInCurrentWeekStudentIds.add(studentCoursesEntry.getKey());
                    continue;
                }
                long count = haveCourseStudents.stream().filter(e -> (GroupType.PRACTICE.equals(e.getGroupType()) ||
                        GroupType.VIP.equals(e.getGroupType()))
                        &&nextMonday.after(e.getCourseStartTime())).count();
                if(count>0){
                    haveClassStudentIds.add(studentCoursesEntry.getKey());
                }else{
                    noClassStudentIds.add(studentCoursesEntry.getKey());
                }
            }

            Set<Integer> allServeStudentIds=new HashSet<>();
            allServeStudentIds.addAll(haveClassStudentIds);
            allServeStudentIds.addAll(noClassStudentIds);

            Map<Integer, List<StudentServeCourseHomeworkDto>> studentHomeworkMap=new HashMap<>();
            Map<Integer, List<ExtracurricularExercisesReply>> studentExercisesMap=new HashMap<>();

            if (!CollectionUtils.isEmpty(allServeStudentIds)){
                List<StudentServeCourseHomeworkDto> studentHomeworks = studentCourseHomeworkDao.findStudentHomeworkWithCourseDateRange(monDayDate.toString(), sunDayDate.toString(), new ArrayList<>(allServeStudentIds));
                List<StudentServeCourseHomeworkDto> studentHomeworksTmp =new ArrayList<>();
                for (StudentServeCourseHomeworkDto studentHomework : studentHomeworks) {
                    LocalDate courseStartLocalDate = LocalDateTime.ofInstant(studentHomework.getCourseStartTime().toInstant(), DateUtil.zoneId).toLocalDate();
                    switch (courseStartLocalDate.getDayOfWeek()){
                        case SATURDAY:
                            if(DateUtil.daysBetween(studentHomework.getCourseStartTime(), studentHomework.getHomeworkCreateTime())<4){
                                studentHomeworksTmp.add(studentHomework);
                            }
                            break;
                        case SUNDAY:
                            if(DateUtil.daysBetween(studentHomework.getCourseStartTime(), studentHomework.getHomeworkCreateTime())<3){
                                studentHomeworksTmp.add(studentHomework);
                            }
                            break;
                        default:
                            LocalDate homeworkCreateTime = LocalDateTime.ofInstant(studentHomework.getHomeworkCreateTime().toInstant(), DateUtil.zoneId).toLocalDate();
                            if(courseStartLocalDate.get(DateUtil.weekFields.weekOfYear())==homeworkCreateTime.get(DateUtil.weekFields.weekOfYear())){
                                studentHomeworksTmp.add(studentHomework);
                            }
                            break;
                    }
                }
                studentHomeworkMap = studentHomeworksTmp.stream()
                        .collect(Collectors.groupingBy(StudentServeCourseHomeworkDto::getUserId));
            }

            if(!CollectionUtils.isEmpty(allServeStudentIds)){
                List<ExtracurricularExercisesReply> studentExercises = extracurricularExercisesReplyDao.getStudentExercisesWithTimeZone(monDayDate.toString(), sunDayDate.toString(), new ArrayList<>(allServeStudentIds));
                studentExercisesMap = studentExercises.stream().collect(Collectors.groupingBy(ExtracurricularExercisesReply::getUserId));
            }

            for (Map.Entry<Integer, List<StudentServeDto>> studentCoursesEntry : studentCoursesMap.entrySet()) {

                if(dontServeInCurrentWeekStudentIds.contains(studentCoursesEntry.getKey())){
                    continue;
                }

                StudentExtracurricularExercisesSituation studentExtracurricularExercisesSituation=new StudentExtracurricularExercisesSituation();
                studentExtracurricularExercisesSituation.setStudentId(studentCoursesEntry.getKey());
                studentExtracurricularExercisesSituation.setTeacherId(studentCoursesEntry.getValue().get(0).getTeacherId());
                studentExtracurricularExercisesSituation.setExpectExercisesNum(1);
                studentExtracurricularExercisesSituation.setActualExercisesNum(0);
                studentExtracurricularExercisesSituation.setExercisesReplyNum(0);
                studentExtracurricularExercisesSituation.setExercisesMessageNum(0);
                studentExtracurricularExercisesSituation.setExercisesMessageTimelyNum(0);
                studentExtracurricularExercisesSituation.setWeekOfYear(nowDate.get(DateUtil.weekFields.weekOfYear()));
                studentExtracurricularExercisesSituation.setMonday(DateUtil.stringToDate(monDayDate.toString(), "yyyy-MM-dd"));
                studentExtracurricularExercisesSituation.setSunday(DateUtil.stringToDate(sunDayDate.toString(), "yyyy-MM-dd"));

                if(haveClassStudentIds.contains(studentCoursesEntry.getKey())){
                    studentExtracurricularExercisesSituation.setServeType("HOMEWORK");
                }

                if(noClassStudentIds.contains(studentCoursesEntry.getKey())){
                    studentExtracurricularExercisesSituation.setServeType("EXERCISE");
                }

                List<StudentServeCourseHomeworkDto> studentHomeworks = studentHomeworkMap.get(studentCoursesEntry.getKey());
                if(!CollectionUtils.isEmpty(studentHomeworks)){
                    studentExtracurricularExercisesSituation.setActualExercisesNum(1);
                    long replyNum = studentHomeworks.stream().filter(e -> YesOrNoEnum.YES.equals(e.getStatus())).count();
                    studentExtracurricularExercisesSituation.setExercisesReplyNum(replyNum>0?1:0);
                    long haveSubmitTimes = studentHomeworks.stream().filter(e -> Objects.nonNull(e.getSubmitTime())).count();
                    if(replyNum>0&&haveSubmitTimes>0){
                        Date lastSubmitTime = studentHomeworks.stream().filter(e->Objects.nonNull(e.getSubmitTime())).max(Comparator.comparing(StudentServeCourseHomeworkDto::getSubmitTime)).get().getSubmitTime();
                        studentExtracurricularExercisesSituation.setLastSubmitTime(lastSubmitTime);
                    }
                    int exercisesMessageNum=0;
                    int exercisesMessageTimelyNum=0;
                    for (StudentServeCourseHomeworkDto studentHomework : studentHomeworks) {
                        if(!YesOrNoEnum.YES.equals(studentHomework.getStatus())){
                            continue;
                        }
                        if(!YesOrNoEnum.YES.equals(studentHomework.getIsReplied())){
                            continue;
                        }
                        exercisesMessageNum+=1;
                        if(new Integer(1).equals(studentHomework.getIsRepliedTimely())){
                            exercisesMessageTimelyNum+=1;
                        }
                    }
                    studentExtracurricularExercisesSituation.setExercisesMessageNum(exercisesMessageNum>0?1:0);
                    studentExtracurricularExercisesSituation.setExercisesMessageTimelyNum(exercisesMessageTimelyNum>0?1:0);
                }

                List<ExtracurricularExercisesReply> studentExercises = studentExercisesMap.get(studentCoursesEntry.getKey());
                if(!CollectionUtils.isEmpty(studentExercises)){
                    studentExtracurricularExercisesSituation.setActualExercisesNum(1);
                    long replyNum = studentExercises.stream().filter(e -> e.getStatus()==1).count();
                    if(studentExtracurricularExercisesSituation.getExercisesReplyNum()<=0){
                        studentExtracurricularExercisesSituation.setExercisesReplyNum(replyNum>0?1:0);
                    }
                    long haveSubmitTimes = studentExercises.stream().filter(e->Objects.nonNull(e.getSubmitTime())).count();
                    if(replyNum>0&&haveSubmitTimes>0){
                        Date lastSubmitTime = studentExercises.stream().filter(e->Objects.nonNull(e.getSubmitTime())).max(Comparator.comparing(ExtracurricularExercisesReply::getSubmitTime)).get().getSubmitTime();
                        if(Objects.isNull(studentExtracurricularExercisesSituation.getLastSubmitTime())||lastSubmitTime.after(studentExtracurricularExercisesSituation.getLastSubmitTime())){
                            studentExtracurricularExercisesSituation.setLastSubmitTime(lastSubmitTime);
                        }
                    }
                    int exercisesMessageNum=0;
                    int exercisesMessageTimelyNum=0;
                    for (ExtracurricularExercisesReply studentHomework : studentExercises) {
                        if(!new Integer(1).equals(studentHomework.getStatus())){
                            continue;
                        }
                        if(!new Integer(1).equals(studentHomework.getIsReplied())){
                            continue;
                        }
                        exercisesMessageNum+=1;
                        if(new Integer(1).equals(studentHomework.getIsRepliedTimely())){
                            exercisesMessageTimelyNum+=1;
                        }
                    }
                    if(studentExtracurricularExercisesSituation.getExercisesMessageNum()<=0){
                        studentExtracurricularExercisesSituation.setExercisesMessageNum(exercisesMessageNum>0?1:0);
                    }
                    if(studentExtracurricularExercisesSituation.getExercisesMessageTimelyNum()<=0){
                        studentExtracurricularExercisesSituation.setExercisesMessageTimelyNum(exercisesMessageTimelyNum>0?1:0);
                    }
                }
                results.add(studentExtracurricularExercisesSituation);
            }

            currentPage=currentPage.add(BigDecimal.ONE);
        }

        studentExtracurricularExercisesSituationDao.deleteByMonday(monDayDate.toString());

        BigDecimal currentPage1=BigDecimal.ONE,
                pageSize1=new BigDecimal(10000),
                total1=new BigDecimal(results.size()),
                totalPage1=total1.divide(pageSize1, BigDecimal.ROUND_UP);

        while (currentPage1.compareTo(totalPage1)<=0){
            List<StudentExtracurricularExercisesSituation> rows=results.stream().skip(pageSize1.multiply(currentPage1.subtract(BigDecimal.ONE)).longValue()).limit(pageSize1.longValue()).collect(Collectors.toList());
            studentExtracurricularExercisesSituationDao.batchInsert(rows);
            currentPage1=currentPage1.add(BigDecimal.ONE);
        }
    }

    @Override
    public Map<String, Object> checkeIsAssignHomework(Long courseScheduleId, String studentIdsStr) {
        Map<String, Object> result=new HashMap<>();
        if(Objects.isNull(courseScheduleId)&&Objects.isNull(studentIdsStr)){
            result.put("isAssignHomework", 0);
            return result;
        }
        List<Integer> studentIds=new ArrayList<>();
        if(StringUtils.isNotBlank(studentIdsStr)){
            studentIds= Arrays.asList(studentIdsStr.split(",")).stream().map(id->Integer.valueOf(id)).collect(Collectors.toList());
        }

        LocalDate localDate=LocalDate.now();

        if(Objects.nonNull(courseScheduleId)){
            CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
            if(Objects.isNull(courseSchedule)){
                result.put("isAssignHomework", 0);
                return result;
            }
            localDate=LocalDateTime.ofInstant(courseSchedule.getClassDate().toInstant(), DateUtil.zoneId).toLocalDate();
            List<CourseScheduleStudentPayment> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.findByCourseSchedule(courseScheduleId);
            if(CollectionUtils.isEmpty(courseScheduleStudentPayments)){
                result.put("isAssignHomework", 0);
                return result;
            }
            studentIds = courseScheduleStudentPayments.stream().map(CourseScheduleStudentPayment::getUserId).collect(Collectors.toList());
        }

        LocalDate monDayDate = localDate.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
        LocalDate sunDayDate = localDate.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.SUNDAY.getValue());

        Set<Integer> hss = studentCourseHomeworkDao.checkStudentHaveHomeworkInDateRange(monDayDate.toString(), sunDayDate.toString(), studentIds);
        Set<Integer> ess = extracurricularExercisesReplyDao.checkStudentHaveExercisesInDateRange(monDayDate.toString(), sunDayDate.toString(), studentIds);
        for (Integer studentId : studentIds) {
            if(!hss.contains(studentId)&&!ess.contains(studentId)){
                result.put("isAssignHomework", 0);
                return result;
            }
        }
        result.put("isAssignHomework", 1);
        return result;
    }
}
