package com.xjrsoft.module.banding.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.ArchivesStatusEnum;
import com.xjrsoft.common.enums.DeleteMark;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.enums.RoleEnum;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.module.banding.dto.BandingTaskClassStudentPageDto;
import com.xjrsoft.module.banding.dto.ChangeClassDto;
import com.xjrsoft.module.banding.dto.StudentDto;
import com.xjrsoft.module.banding.entity.BandingTask;
import com.xjrsoft.module.banding.entity.BandingTaskClass;
import com.xjrsoft.module.banding.entity.BandingTaskClassStudent;
import com.xjrsoft.module.banding.mapper.BandingTaskClassMapper;
import com.xjrsoft.module.banding.mapper.BandingTaskClassStudentMapper;
import com.xjrsoft.module.banding.mapper.BandingTaskMapper;
import com.xjrsoft.module.banding.service.IBandingTaskClassStudentService;
import com.xjrsoft.module.banding.vo.BandingTaskClassSureListVo;
import com.xjrsoft.module.base.entity.BaseClass;
import com.xjrsoft.module.base.entity.BaseMajorSet;
import com.xjrsoft.module.base.mapper.BaseMajorSetMapper;
import com.xjrsoft.module.base.service.IBaseClassService;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.service.IUserRoleRelationService;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.student.dto.BaseNewStudentPageDto;
import com.xjrsoft.module.student.entity.BaseClassMajorSet;
import com.xjrsoft.module.student.entity.BaseNewStudent;
import com.xjrsoft.module.student.entity.BaseStudent;
import com.xjrsoft.module.student.entity.BaseStudentFamily;
import com.xjrsoft.module.student.entity.BaseStudentSchoolRoll;
import com.xjrsoft.module.student.mapper.BaseClassMajorSetMapper;
import com.xjrsoft.module.student.service.IBaseNewStudentService;
import com.xjrsoft.module.student.service.IBaseStudentFamilyService;
import com.xjrsoft.module.student.service.IBaseStudentSchoolRollService;
import com.xjrsoft.module.student.service.IBaseStudentService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
* @title: 新生分班任务
* @Author dzx
* @Date: 2024-07-01
* @Version 1.0
*/
@Service
@AllArgsConstructor
public class BandingTaskClassStudentServiceImpl extends MPJBaseServiceImpl<BandingTaskClassStudentMapper, BandingTaskClassStudent> implements IBandingTaskClassStudentService {
    private final BandingTaskClassMapper taskClassMapper;
    private final BandingTaskMapper bandingTaskMapper;
    private final IBaseNewStudentService newStudentService;

    private final IBaseClassService classService;
    private final BaseClassMajorSetMapper classMajorSetMapper;
    private final CommonPropertiesConfig propertiesConfig;
    private final IUserService userService;
    private final IUserRoleRelationService roleRelationService;
    private final IBaseStudentSchoolRollService schoolRollService;
    private final IBaseStudentService studentService;
    private final IBaseStudentFamilyService familyService;
    private final BaseMajorSetMapper majorSetMapper;
    private final RedisUtil redisUtil;
    @Override
    public Boolean add(BandingTaskClassStudent bandingTaskClass) {
        bandingTaskClass.setCreateDate(new Date());
        this.baseMapper.insert(bandingTaskClass);
        return true;
    }

    @Override
    public Boolean update(BandingTaskClassStudent bandingTaskClass) {
        bandingTaskClass.setModifyDate(new Date());
        this.baseMapper.updateById(bandingTaskClass);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(List<Long> ids) {
        this.baseMapper.deleteBatchIds(ids);
        return true;
    }

    @Transactional
    @Override
    public Boolean changeClass(ChangeClassDto dto) {
        this.baseMapper.delete(
                new QueryWrapper<BandingTaskClassStudent>().lambda()
                        .in(BandingTaskClassStudent::getNewStudentId, dto.getNewStudentIds())
        );
        List<BandingTaskClassStudent> dataList = new ArrayList<>();
        long createUserId = StpUtil.getLoginIdAsLong();
        for (Long newStudentId : dto.getNewStudentIds()) {
            dataList.add(
                    new BandingTaskClassStudent(){{
                        setCreateDate(new Date());
                        setNewStudentId(newStudentId);
                        setBandingTaskClassId(dto.getBandingTaskClassId());
                        setCreateUserId(createUserId);
                        setStatus(0);
                        if(dto.getIsHandle() != null && dto.getIsHandle() == 1){
                            setStatus(1);
                        }
                    }}
            );
        }
        if(!dataList.isEmpty()){
            this.saveBatch(dataList);
        }
        if(dto.getIsHandle() != null && dto.getIsHandle() == 1){
            BandingTaskClass taskClass = taskClassMapper.selectById(dto.getBandingTaskClassId());
            List<Long> studentIds = dataList.stream().map(BandingTaskClassStudent::getNewStudentId).collect(Collectors.toList());
            List<BaseNewStudent> list = newStudentService.list(
                    new QueryWrapper<BaseNewStudent>().lambda()
                            .in(BaseNewStudent::getId, studentIds)
            );
            List<BaseNewStudent> updateList = new ArrayList<>();
            for (BaseNewStudent student : list) {
                student.setStatus(1);
                updateList.add(student);
            }
            if(!updateList.isEmpty()){
                newStudentService.updateBatchById(updateList);
            }
            {
                Date createDate = new Date();
                BandingTask bandingTask = bandingTaskMapper.selectById(taskClass.getBandingTaskId());
                List<BandingTaskClassSureListVo> classSure = taskClassMapper.getClassSure(new BandingTaskClassStudentPageDto() {{
                    setBandingTaskId(taskClass.getBandingTaskId());
                }});

                Map<Long, Integer> classTotal = classSure.stream().collect(Collectors.toMap(BandingTaskClassSureListVo::getId, BandingTaskClassSureListVo::getNumber));
                Map<Long, Integer> classBoy = classSure.stream().collect(Collectors.toMap(BandingTaskClassSureListVo::getId, BandingTaskClassSureListVo::getMaleCount));
                Map<Long, Integer> classGirl = classSure.stream().collect(Collectors.toMap(BandingTaskClassSureListVo::getId, BandingTaskClassSureListVo::getFemaleCount));


                //生成班级数据
                Map<Long, Long> classMap = new HashMap<>();
                Map<Long, Long> taskClassMajorMap = new HashMap<>();
                //查询出需要新增的班级信息
                Map<Long, Long> majorDeptMap = majorSetMapper.selectList(
                        new QueryWrapper<BaseMajorSet>()
                ).stream().collect(Collectors.toMap(BaseMajorSet::getId, BaseMajorSet::getDepartmentId));
                //先查询是否已经存在这个班级，如果存在就更新，不存在就新增
                BaseClass baseClass = classService.getOne(
                        new QueryWrapper<BaseClass>().lambda()
                                .eq(BaseClass::getName, taskClass.getName())
                                .eq(BaseClass::getGradeId, bandingTask.getGradeId())
                                .eq(BaseClass::getEnrollType, bandingTask.getEnrollType())
                );
                if(baseClass == null){
                    baseClass = new BaseClass() {{
                        setName(taskClass.getName());
                        setClassroomId(taskClass.getClassroomId());
                        setTeacherId(taskClass.getTeacherId());
                        setIsGraduate(1);
                        setMajorSetId(taskClass.getMajorSetId());
                        setOrgId(majorDeptMap.get(taskClass.getMajorSetId()));
                        setIsOrderClass(taskClass.getIsOrderClass()==null?0:taskClass.getIsOrderClass().intValue());
                        setGradeId(bandingTask.getGradeId());
                        setDeleteMark(DeleteMark.NODELETE.getCode());
                        setEnrollType(bandingTask.getEnrollType());
                        setCreateDate(new Date());
                    }};

                    classService.save(baseClass);

                    BaseClassMajorSet majorSet = new BaseClassMajorSet() {{
                        setCreateDate(createDate);
                        setMajorSetId(taskClass.getMajorSetId());
                        setPlanTotalStudent(taskClass.getNumber());
                        setTotalStudent(classTotal.get(taskClass.getId()));
                        setBoyNum(classBoy.get(taskClass.getId()));
                        setGirlNum(classGirl.get(taskClass.getId()));
                    }};
                    majorSet.setClassId(baseClass.getId());
                    classMajorSetMapper.insert(majorSet);
                }


                classMap.put(taskClass.getId(), baseClass.getId());
                taskClassMajorMap.put(taskClass.getId(), taskClass.getMajorSetId());

                /**
                 * 新增学生数据
                 * 1、新增用户xjr_user，需要先查询用户信息是否已经存在
                 * 2、新增用户与角色的关系xjr_user_role_relation
                 * 3、新增学生基本信息base_student
                 * 4、新增学籍信息表base_student_school_roll
                 * 5、新增家庭信息表base_student_family
                 */
                LocalDateTime now = LocalDateTime.now();
                List<String> idNumbers = updateList.stream().map(BaseNewStudent::getCredentialNumber).collect(Collectors.toList());
                List<User> students = userService.list(
                        new MPJLambdaWrapper<User>()
                                .disableLogicDel()
                                .in(User::getCredentialNumber, idNumbers)
                                .orderByAsc(User::getCreateDate)
                );
                Map<String, Long> userSet = new HashMap<>();
                Map<String, Integer> userDeleteMarkMap = new HashMap<>();
                for (User student : students) {
                    userSet.put(student.getCredentialNumber(), student.getId());
                    userDeleteMarkMap.put(student.getCredentialNumber(), student.getDeleteMark());
                }
                Map<Long, Long> studentClassRelation = dataList.stream().collect(Collectors.toMap(BandingTaskClassStudent::getNewStudentId, BandingTaskClassStudent::getBandingTaskClassId));
                for (BaseNewStudent student : updateList) {
                    if(userSet.containsKey(student.getCredentialNumber())){
                        schoolRollService.updateStudentClass(classMap.get(studentClassRelation.get(student.getId())), userSet.get(student.getCredentialNumber()));
                        Integer deleteMark = userDeleteMarkMap.get(student.getCredentialNumber());
                        if(deleteMark != null && deleteMark == 1){
                            userService.recoveryStudentInfo(userSet.get(student.getCredentialNumber()));
                        }
                        continue;
                    }
                    LocalDate birthDate = getBirthDate(student.getCredentialNumber());
                    User xjrUser = new User() {{
                        setCreateDate(now);
                        setPassword(BCrypt.hashpw(propertiesConfig.getDefaultPassword(), BCrypt.gensalt()));
                        setName(student.getName());
                        setUserName(student.getCredentialNumber());
                        setCredentialNumber(student.getCredentialNumber());
                        setCredentialType("ZZLS10007");
                        setMobile(student.getMobile());
                        setEnabledMark(EnabledMark.DISABLED.getCode());
                        setGender(student.getGender());
                        setIsChangePassword(1);
                        setBirthDate(birthDate.atStartOfDay());
                    }};
                    userService.save(xjrUser);

                    UserRoleRelation userRoleRelation = new UserRoleRelation() {{
                        setRoleId(RoleEnum.STUDENT.getCode());
                        setUserId(xjrUser.getId());
                    }};
                    roleRelationService.save(userRoleRelation);

                    BaseStudent baseStudent = new BaseStudent() {{
                        setUserId(xjrUser.getId());
                        setCreateDate(now);
                        setStudentId(student.getCredentialNumber());
                        if(student.getHeight() != null){
                            setHeight(student.getHeight().doubleValue());
                        }
                        if(student.getWeight() != null){
                            setWeight(student.getWeight().doubleValue());
                        }
                    }};
                    studentService.save(baseStudent);

                    BaseStudentSchoolRoll schoolRoll = new BaseStudentSchoolRoll() {{
                        setUserId(xjrUser.getId());
                        if(student.getScore() != null){
                            setGraduatedScore(student.getScore().doubleValue());
                        }

                        setGraduatedUniversity(student.getGraduateSchool());
                        setClassId(classMap.get(studentClassRelation.get(student.getId())));
                        setMajorSetId(taskClassMajorMap.get(studentClassRelation.get(student.getId())));
                        setStduyStatus(student.getStduyStatus());
                        setEnrollType(bandingTask.getEnrollType());
                        setStudentSource(student.getSource());
                        setGradeId(bandingTask.getGradeId());
                        setArchivesStatus(ArchivesStatusEnum.FB2901.getCode());
                        setCreateDate(now);
                    }};
                    schoolRollService.save(schoolRoll);

                    BaseStudentFamily studentFamily = new BaseStudentFamily() {{
                        setCreateDate(now);
                        setUserId(xjrUser.getId());
                        setTelephone(student.getFamilyMobile());
                        setAddress(student.getFamilyAddress());
                    }};
                    familyService.save(studentFamily);
                }
            }
            CompletableFuture.runAsync(() -> {
                List<User> userList = userService.list();
                redisUtil.set(GlobalConstant.USER_CACHE_KEY, userList);

                List<UserRoleRelation> userRoleRelationList = roleRelationService.list(Wrappers.lambdaQuery(UserRoleRelation.class));
                redisUtil.set(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, userRoleRelationList);
            });
        }
        return true;
    }

    LocalDate getBirthDate(String idCardNumber){
        // 获取出生日期前6位，即yyyyMM
        String birthdayString = idCardNumber.substring(6, 14);

        // 将字符串解析为LocalDate对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        try {
            LocalDate parse = LocalDate.parse(birthdayString, formatter);
            return parse;
        }catch (Exception e){
            throw new MyException("身份证号填写错误，无法提取出生日期");
        }
    }

    @Override
    public Boolean removeStudent(ChangeClassDto dto) {
        this.baseMapper.delete(
                new QueryWrapper<BandingTaskClassStudent>().lambda()
                        .in(BandingTaskClassStudent::getNewStudentId, dto.getNewStudentIds())
                        .eq(BandingTaskClassStudent::getBandingTaskClassId, dto.getBandingTaskClassId())
        );
        return true;
    }

    @Override
    public List<BaseNewStudentPageDto> satisfyStudent(StudentDto dto) {
        BandingTaskClass taskClass = taskClassMapper.selectById(dto.getBandingTaskClassId());
        dto.setBandingTaskId(taskClass.getBandingTaskId());
        return this.baseMapper.satisfyStudent(dto);
    }

    @Override
    public List<BaseNewStudentPageDto> surplusStudent(StudentDto dto) {
        BandingTaskClass taskClass = taskClassMapper.selectById(dto.getBandingTaskClassId());
        dto.setMajorSetId(taskClass.getMajorSetId());
        dto.setBandingTaskId(taskClass.getBandingTaskId());
        return this.baseMapper.surplusStudent(dto);
    }

    @Override
    public Boolean insertStudent(ChangeClassDto dto) {
        List<BandingTaskClassStudent> dataList = new ArrayList<>();
        Date createDate = new Date();
        for (Long newStudentId : dto.getNewStudentIds()) {
            dataList.add(
                    new BandingTaskClassStudent(){{
                        setNewStudentId(newStudentId);
                        setCreateDate(createDate);
                        setStatus(0);
                        setBandingTaskClassId(dto.getBandingTaskClassId());
                    }}
            );
        }
        if(!dataList.isEmpty()){
           this.saveBatch(dataList);
        }

        return true;
    }

}
