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

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.ym.mec.biz.dal.dao.CourseScheduleDao;
import com.ym.mec.biz.dal.dao.CourseScheduleStudentPaymentDao;
import com.ym.mec.biz.dal.dao.MusicGroupDao;
import com.ym.mec.biz.dal.dao.PracticeGroupDao;
import com.ym.mec.biz.dal.dao.StudentDao;
import com.ym.mec.biz.dal.dao.StudentRegistrationDao;
import com.ym.mec.biz.dal.dto.StudentClassInfoDto;
import com.ym.mec.biz.dal.dto.StudentCourseTimesDto;
import com.ym.mec.biz.dal.dto.StudentTeacherCourseDto;
import com.ym.mec.biz.dal.entity.CooperationOrgan;
import com.ym.mec.biz.dal.entity.CourseSchedule;
import com.ym.mec.biz.dal.entity.Student;
import com.ym.mec.biz.dal.enums.FivePlusGradeEnum;
import com.ym.mec.biz.dal.enums.GradeTypeEnum;
import com.ym.mec.biz.dal.enums.PeriodEnum;
import com.ym.mec.biz.dal.enums.SixPlusGradeEnum;
import com.ym.mec.biz.dal.page.StudentQueryInfo;
import com.ym.mec.biz.service.StudentService;
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;

@Service
public class StudentServiceImpl extends BaseServiceImpl<Integer, Student> implements StudentService {

    @Autowired
    private StudentDao studentDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;

    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;

    @Autowired
    private StudentRegistrationDao studentRegistrationDao;

    @Autowired
    private MusicGroupDao musicGroupDao;

    @Autowired
    private PracticeGroupDao practiceGroupDao;

    @Override
    public BaseDAO<Integer, Student> getDAO() {
        return studentDao;
    }

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

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long upSet(Student student) {
        if (studentDao.get(student.getUserId()) == null) {
            return studentDao.insert(student);
        } else {
            student.setUpdateTime(new Date());
            studentDao.update(student);
            return student.getUserId().longValue();
        }
    }

    @Override
    public boolean updateOperatingTempTag() {
        List<StudentCourseTimesDto> studentCourseTimesDtoList = courseScheduleStudentPaymentDao.queryStudentNotStartCourseTimesOfOnline();
        Map<Integer, StudentCourseTimesDto> map = studentCourseTimesDtoList.stream().collect(Collectors.toMap(StudentCourseTimesDto::getUserId, s -> s));
        //查询服务指标为0的用户
        List<Student> unlabeledStudentList = studentDao.queryByOperatingTempTag(0);

        List<Student> updateStudentList = new ArrayList<Student>();
        StudentCourseTimesDto dto = null;
        for (Student s : unlabeledStudentList) {
            if (s.getOperatingTag() == 1) {
                continue;
            }
            dto = map.get(s.getUserId());
            if (dto != null) {
                if (dto.getTotalCourseTimes() > 0 && dto.getTotalCourseTimes() != dto.getFreePracticeCourseTimes()) {
                    s.setOperatingTempTag(1);
                    s.setOperatingTag(1);
                    updateStudentList.add(s);
                }
            }
        }

        List<Student> labeledStudentList = studentDao.queryByOperatingTempTag(1);
        for (Student s : labeledStudentList) {
            dto = map.get(s.getUserId());
            if (dto == null || dto.getTotalCourseTimes() == dto.getFreePracticeCourseTimes()) {
                s.setOperatingTempTag(0);
                s.setOperatingTag(0);
                updateStudentList.add(s);
            }
        }

        if (updateStudentList.size() > 0) {
            studentDao.batchUpdate(updateStudentList);
        }

        return true;
    }

    @Override
    public void initTeacherId() {
        List<StudentTeacherCourseDto> allStudentCourseInfo = courseScheduleStudentPaymentDao.findAllStudentCourseInfo();
        Map<Integer, List<StudentTeacherCourseDto>> studentCoursesMap = allStudentCourseInfo.stream().collect(Collectors.groupingBy(StudentTeacherCourseDto::getStudentId));
        List<Student> students = new ArrayList<>();
        for (Map.Entry<Integer, List<StudentTeacherCourseDto>> studentCoursesEntry : studentCoursesMap.entrySet()) {
            Map<CourseSchedule.CourseScheduleType, List<StudentTeacherCourseDto>> courseTypeCourseMap = studentCoursesEntry.getValue().stream().collect(Collectors.groupingBy(StudentTeacherCourseDto::getCourseScheduleType));
            List<StudentTeacherCourseDto> practiceCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.PRACTICE);
            if (!CollectionUtils.isEmpty(practiceCourses)) {
                practiceCourses.sort(Comparator.comparing(StudentTeacherCourseDto::getClassesStartTime).reversed());
                Student student = new Student(studentCoursesEntry.getKey());
                student.setTeacherId(practiceCourses.get(0).getTeacherId());
                students.add(student);
                continue;
            }

            List<StudentTeacherCourseDto> vipCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.VIP);
            if (!CollectionUtils.isEmpty(vipCourses)) {
                Student student = new Student(studentCoursesEntry.getKey());
                vipCourses.sort(Comparator.comparing(StudentTeacherCourseDto::getClassesStartTime).reversed());
                student.setTeacherId(vipCourses.get(0).getTeacherId());
                students.add(student);
                continue;
            }

            List<StudentTeacherCourseDto> singleCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.SINGLE);
            if (!CollectionUtils.isEmpty(singleCourses)) {
                Student student = new Student(studentCoursesEntry.getKey());
                singleCourses.sort(Comparator.comparing(StudentTeacherCourseDto::getClassesStartTime).reversed());
                student.setTeacherId(singleCourses.get(0).getTeacherId());
                students.add(student);
                continue;
            }

            List<StudentTeacherCourseDto> mixCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.MIX);
            if (!CollectionUtils.isEmpty(mixCourses)) {
                Student student = new Student(studentCoursesEntry.getKey());
                mixCourses.sort(Comparator.comparing(StudentTeacherCourseDto::getClassesStartTime).reversed());
                student.setTeacherId(mixCourses.get(0).getTeacherId());
                students.add(student);
                continue;
            }
        }
        if (!CollectionUtils.isEmpty(students)) {
            studentDao.batchUpdate(students);
        }
        students = null;
        studentCoursesMap = null;
        allStudentCourseInfo = null;
    }

    @Override
    public void batchUpdateAdviser(Integer teacherId, String studentIds) {
        studentDao.batchUpdateAdviser(teacherId, studentIds);
    }

    @Override
    public Integer updateGrade() {
        return studentDao.updateGrade();
    }

    @Override
    public String getStudentGrade(GradeTypeEnum gradeType, Integer gradeNum) {
        String grade = "";
        if (GradeTypeEnum.FIVE_PLUS.equals(gradeType)) {
            FivePlusGradeEnum[] fivePlusGradeEnums = FivePlusGradeEnum.values();
            for (FivePlusGradeEnum fivePlusGradeEnum : fivePlusGradeEnums) {
                if (!fivePlusGradeEnum.getCode().equals(gradeNum)) continue;
                grade = fivePlusGradeEnum.getDesc();
            }
        }
        if (GradeTypeEnum.SIX_PLUS.equals(gradeType)) {
            SixPlusGradeEnum[] sixPlusGradeEnums = SixPlusGradeEnum.values();
            for (SixPlusGradeEnum sixPlusGradeEnum : sixPlusGradeEnums) {
                if (!sixPlusGradeEnum.getCode().equals(gradeNum)) continue;
                grade = sixPlusGradeEnum.getDesc();
            }
        }
        return grade;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStudentTeacherAndSubject() {
        //更新新学员状态
        List<Integer> newStudentIds = studentDao.getHasVipCourseStudentIdsFromNewStudents();
        if(!CollectionUtils.isEmpty(newStudentIds)){
            studentDao.updateNewStudentsToOld(newStudentIds);
        }

        List<Student> noTeacherOrNoSubjectStudent = studentDao.getNoTeacherOrNoSubjectStudent();
        if (CollectionUtils.isEmpty(noTeacherOrNoSubjectStudent)) {
            return;
        }
        List<Integer> studentIds = noTeacherOrNoSubjectStudent.stream().map(Student::getUserId).collect(Collectors.toList());
        List<StudentClassInfoDto> studentClassInfo = studentRegistrationDao.getStudentClassInfo(studentIds);
        if (CollectionUtils.isEmpty(studentClassInfo)) {
            return;
        }
        //更新学员指导老师和声部信息
        List<Student> needUpdateStudents = new ArrayList<>();
        Map<Integer, List<StudentClassInfoDto>> scm = studentClassInfo.stream().collect(Collectors.groupingBy(StudentClassInfoDto::getStudentId));
        for (Student student : noTeacherOrNoSubjectStudent) {
            if (!scm.containsKey(student.getUserId())) {
                continue;
            }
            List<StudentClassInfoDto> studentClassInfoDtos = scm.get(student.getUserId());
            StudentClassInfoDto studentClassInfoDto = studentClassInfoDtos.stream().max(Comparator.comparing(StudentClassInfoDto::getClassGroupId)).get();
            if (Objects.isNull(student.getTeacherId())) {
                student.setTeacherId(studentClassInfoDto.getMainTeacherId());
            }
            if (StringUtils.isBlank(student.getSubjectIdList())) {
                student.setSubjectIdList(studentClassInfoDto.getSubjectId().toString());
            }
            if (Objects.isNull(student.getCooperationOrganId())) {
                student.setCooperationOrganId(studentClassInfoDto.getCooperationOrganId());
            }
            needUpdateStudents.add(student);
        }
        if (!CollectionUtils.isEmpty(needUpdateStudents)) {
            studentDao.batchUpdate(needUpdateStudents);
        }
    }

    @Override
    public CooperationOrgan getStudentEduTeacher(Integer userId) {
        CooperationOrgan cooperationOrgan = musicGroupDao.findUserMusicGroupCooperationOrgan(userId);
        if (cooperationOrgan != null) {
            return cooperationOrgan;
        }
        cooperationOrgan = new CooperationOrgan();
        String studentEduTeacher = courseScheduleStudentPaymentDao.getStudentEduTeacher(userId);
        if (studentEduTeacher == null) {
            studentEduTeacher = practiceGroupDao.getStudentEduTeacher(userId);
        }
        cooperationOrgan.setLinkman(studentEduTeacher);
        return cooperationOrgan;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMemberRank(Integer userId, Integer rankSettingId, PeriodEnum periodEnum, int quantityPerPeriod) {
        // 添加会员有效时长
        Student student = this.get(userId);
        if (student == null) {
            throw new BizException("学员信息不存在");
        }
        
        Date nowDate = new Date();
        if (student.getMemberRankSettingId() == null || nowDate.after(student.getMembershipEndTime())) {
        	switch (periodEnum) {
    		case DAY:
                student.setMembershipEndTime(DateUtil.addDays(nowDate, quantityPerPeriod));
    			break;
    		case MONTH:
    		case YEAR_HALF:
                student.setMembershipEndTime(DateUtil.addMonths(nowDate, quantityPerPeriod));
    			break;
    		case YEAR:
                student.setMembershipEndTime(DateUtil.addYears(nowDate, quantityPerPeriod));
    			break;

    		default:
    			break;
    		}
            student.setMembershipStartTime(nowDate);
        } else {
        	switch (periodEnum) {
    		case DAY:
            	student.setMembershipEndTime(DateUtil.addDays(student.getMembershipEndTime(), quantityPerPeriod));
    			break;
    		case MONTH:
    		case YEAR_HALF:
            	student.setMembershipEndTime(DateUtil.addMonths(student.getMembershipEndTime(), quantityPerPeriod));
    			break;
    		case YEAR:
            	student.setMembershipEndTime(DateUtil.addYears(student.getMembershipEndTime(), quantityPerPeriod));
    			break;

    		default:
    			break;
    		}
        }
        student.setMemberRankSettingId(rankSettingId);
        student.setUpdateTime(nowDate);
        this.update(student);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdateMemberRank(List<Integer> studentIds, Integer rankSettingId, PeriodEnum periodEnum, int quantityPerPeriod) {
        // 添加会员有效时长
        List<Student> studentList = studentDao.findByStudentIds(studentIds);
        Date nowDate = new Date();
        for (Student student : studentList) {
            if (student.getMemberRankSettingId() == null || nowDate.after(student.getMembershipEndTime())) {
                switch (periodEnum) {
                    case DAY:
                        student.setMembershipEndTime(DateUtil.addDays(nowDate, quantityPerPeriod));
                        break;
                    case MONTH:
                    case YEAR_HALF:
                        student.setMembershipEndTime(DateUtil.addMonths(nowDate, quantityPerPeriod));
                        break;
                    case YEAR:
                        student.setMembershipEndTime(DateUtil.addYears(nowDate, quantityPerPeriod));
                        break;
                    default:
                        break;
                }
                student.setMembershipStartTime(nowDate);
            } else {
                switch (periodEnum) {
                    case DAY:
                        student.setMembershipEndTime(DateUtil.addMonths(student.getMembershipEndTime(), quantityPerPeriod));
                        break;
                    case MONTH:
                    case YEAR_HALF:
                        student.setMembershipEndTime(DateUtil.addMonths(student.getMembershipEndTime(), quantityPerPeriod));
                        break;
                    case YEAR:
                        student.setMembershipEndTime(DateUtil.addMonths(student.getMembershipEndTime(), quantityPerPeriod));
                        break;

                    default:
                        break;
                }
            }
            student.setMemberRankSettingId(rankSettingId);
            student.setUpdateTime(nowDate);
        }
        studentDao.batchUpdate(studentList);
    }

    @Override
    public void updateStudentServiceTag(Integer studentId,List<Integer> studentIds,Integer serviceTag) {
        studentDao.updateStudentServiceTag(studentId, studentIds, serviceTag);
    }

    @Override
    public List<String> getStudentNames(List<Integer> studentIdList) {
        return studentDao.getStudentNames(studentIdList);
    }

	@Override
	public Student getLocked(Integer userId) {
		return studentDao.getLocked(userId);
	}

    @Override
    public void cleanMember(Integer userId) {
        studentDao.cleanMember(userId);
    }
}
