package com.qmth.teachcloud.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.teachcloud.common.base.BaseEntity;
import com.qmth.teachcloud.common.bean.dto.BasicStudentExportDto;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
import com.qmth.teachcloud.common.bean.dto.TaskPrintClassDto;
import com.qmth.teachcloud.common.bean.dto.excel.BasicStudentImportDto;
import com.qmth.teachcloud.common.bean.dto.excel.DescribeImportDto;
import com.qmth.teachcloud.common.bean.params.*;
import com.qmth.teachcloud.common.bean.result.BasicStudentResult;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.*;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.TeachBasicEnum;
import com.qmth.teachcloud.common.mapper.BasicStudentMapper;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 学校id 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2021-08-02
 */
@Service
public class BasicStudentServiceImpl extends ServiceImpl<BasicStudentMapper, BasicStudent> implements BasicStudentService {
    @Resource
    private BasicStudentMapper basicStudentMapper;
    @Resource
    private BasicClazzService basicClazzService;
    @Resource
    private BasicMajorService basicMajorService;
    @Resource
    private BasicCollegeService basicCollegeService;
    @Resource
    private BasicRoleDataPermissionService basicRoleDataPermissionService;


    @Override
    public IPage<BasicStudentResult> basicStudentPage(String queryParams, Long collegeId, Long majorId, Set<Long> clazzIdList, int pageNumber, int pageSize) {
        Long schoolId = SystemConstant.convertIdToLong(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        queryParams = SystemConstant.translateSpecificSign(queryParams);
        return basicStudentMapper.findBasicStudentPage(new Page<>(pageNumber, pageSize), queryParams, schoolId, collegeId, majorId, clazzIdList, dpr);
    }

    @Override
    public BasicStudentResult findById(Long id) {
        BasicStudentResult basicStudentResult = null;
        if (SystemConstant.longNotNull(id)) {
            basicStudentResult = this.baseMapper.findById(id);
        }
        return basicStudentResult;
    }

    @Override
    public List<BasicStudentResult> basicStudentList(Long schoolId, Long collegeId, Long majorId, Long clazzId, List<String> studentCodeList) {
        return this.baseMapper.findBasicStudentList(schoolId, collegeId, majorId, clazzId, studentCodeList);
    }

    @Override
    public IPage<BasicStudent> listPage(Page<BasicStudent> page, String classId) {
        List<String> classIds = Arrays.asList(classId.split(","));
        QueryWrapper<BasicStudent> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(BasicStudent::getClazzId, classIds)
                .orderByAsc(BasicStudent::getClazzId)
                .orderByAsc(BasicStudent::getStudentCode);
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    public List<TaskPrintClassDto> listByClass(Long schoolId, Set<String> stringList) {
        return this.baseMapper.listByClass(schoolId, stringList).stream()
                .peek(e -> e.setTeachBasicEnum(TeachBasicEnum.BASIC_CLAZZ))
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long saveBasicStudent(BasicStudentParams basicStudentParams, SysUser requestUser) {
        List<BasicStudentParams> basicStudentParamsList = new ArrayList<>();
        basicStudentParamsList.add(basicStudentParams);
        BasicStudent basicStudent = this.editEntityHelp(basicStudentParamsList, requestUser).get(0);
        this.saveOrUpdate(basicStudent);
        return basicStudent.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdateBasicStudentBatch(List<BasicStudentParams> basicStudentParamsList, SysUser requestUser) {
        this.saveOrUpdateBatch(this.editEntityHelp(basicStudentParamsList, requestUser));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveBasicStudentWithExtrasList(List<BasicStudentExtrasParam> basicStudentExtrasParamList, SysUser requestUser) throws IllegalAccessException {
        Long schoolId = requestUser.getSchoolId();
        // key = collegeName
        Map<String, Long> collegeMap = new HashMap<>();
        // key = schoolId + "-" + majorName
        Map<String, Long> majorMap = new HashMap<>();
        // key = schoolId + "-" + clazzName
        Map<String, Long> clazzMap = new HashMap<>();

        List<BasicStudentParams> addBasicStudentParamsList = new ArrayList<>();
        for (BasicStudentExtrasParam basicStudentExtrasParam : basicStudentExtrasParamList) {
            // 取值
            String studentName = basicStudentExtrasParam.getStudentName();
            String studentCode = basicStudentExtrasParam.getStudentCode();
            String phoneNumber = basicStudentExtrasParam.getPhoneNumber();
            String collegeName = basicStudentExtrasParam.getCollegeName();
            String majorName = basicStudentExtrasParam.getMajorName();
            String clazzName = basicStudentExtrasParam.getClazzName();

            // 学院
            Long collegeId = null;
            if (SystemConstant.strNotNull(collegeName)) {
                BasicCollege basicCollege;
                if (collegeMap.containsKey(collegeName)) {
                    collegeId = collegeMap.get(collegeName);
                } else {
                    basicCollege = basicCollegeService.getOne(new QueryWrapper<BasicCollege>().lambda()
                            .eq(BasicCollege::getSchoolId, schoolId)
                            .eq(BasicCollege::getCollegeName, collegeName));
                    if (Objects.isNull(basicCollege)) {
                        // 新增一个basicCollege
                        BasicCollegeParams basicCollegeParams = new BasicCollegeParams();
                        basicCollegeParams.setCollegeName(collegeName);
                        collegeId = basicCollegeService.saveBasicCollege(basicCollegeParams, requestUser);
                    } else {
                        collegeId = basicCollege.getId();
                    }
                    collegeMap.put(collegeName, collegeId);
                }
            }

            // 专业
            Long majorId = null;
            if (SystemConstant.strNotNull(majorName)) {
                if (!SystemConstant.longNotNull(collegeId)) {
                    throw ExceptionResultEnum.ERROR.exception("学生专业信息缺失学院信息异常");
                }
                String majorKey = schoolId + SystemConstant.HYPHEN + majorName;
                if (majorMap.containsKey(majorKey)) {
                    majorId = majorMap.get(majorKey);
                } else {
                    BasicMajor basicMajor = basicMajorService.getOne(new QueryWrapper<BasicMajor>().lambda()
                            .eq(BasicMajor::getSchoolId, schoolId)
                            .eq(BasicMajor::getName, majorName));
                    if (Objects.isNull(basicMajor)) {
                        // 新增一个major
                        BasicMajorParams basicMajorParams = new BasicMajorParams();
                        basicMajorParams.setMajorName(majorName);
                        basicMajorParams.setCollegeId(collegeId);
                        majorId = basicMajorService.saveBasicMajor(basicMajorParams, requestUser);
                    } else {
                        majorId = basicMajor.getId();
                    }
                    majorMap.put(majorKey, majorId);
                }
            }

            // 班级
            Long clazzId = null;
            if (SystemConstant.strNotNull(clazzName)) {
                if (!SystemConstant.longNotNull(collegeId)) {
                    throw ExceptionResultEnum.ERROR.exception("学生班级信息缺失学院信息异常");
                }
                if (!SystemConstant.longNotNull(majorId)) {
                    throw ExceptionResultEnum.ERROR.exception("学生班级信息缺失专业信息异常");
                }

                String clazzKey = schoolId + SystemConstant.HYPHEN + clazzName;
                if (clazzMap.containsKey(clazzKey)) {
                    clazzId = clazzMap.get(clazzKey);
                } else {
                    // 2022-03-30 字典班级在全校唯一
                    BasicClazz basicClazz = basicClazzService.getOne(new QueryWrapper<BasicClazz>().lambda()
                            .eq(BasicClazz::getSchoolId, schoolId)
                            .eq(BasicClazz::getClazzName, clazzName));
                    if (Objects.isNull(basicClazz)) {
                        // 新增一个clazz
                        BasicClazzParams basicClazzParams = new BasicClazzParams();
                        basicClazzParams.setMajorId(majorId);
                        basicClazzParams.setClazzName(clazzName);
                        clazzId = basicClazzService.saveBasicClazz(basicClazzParams, requestUser);
                    } else {
                        clazzId = basicClazz.getId();
                    }
                    clazzMap.put(clazzKey, clazzId);
                }
            }
            // 学生信息组装
            BasicStudentParams basicStudentParams = new BasicStudentParams();
            basicStudentParams.setStudentName(studentName);
            basicStudentParams.setStudentCode(studentCode);
            basicStudentParams.setPhoneNumber(phoneNumber);
            if (SystemConstant.longNotNull(collegeId)) {
                basicStudentParams.setCollegeId(collegeId);
            }
            if (SystemConstant.longNotNull(majorId)) {
                basicStudentParams.setMajorId(majorId);
            }
            if (SystemConstant.longNotNull(clazzId)) {
                basicStudentParams.setClazzId(clazzId);
            }

            BasicStudent tmp = this.getOne(new QueryWrapper<BasicStudent>().lambda()
                    .eq(BasicStudent::getSchoolId, requestUser.getSchoolId())
                    .eq(BasicStudent::getEnable, true)
                    .eq(BasicStudent::getStudentCode, studentCode));
            if (Objects.nonNull(tmp)) {
                basicStudentParams.setId(tmp.getId());
            }
            addBasicStudentParamsList.add(basicStudentParams);
        }
        List<BasicStudent> basicStudentList = this.editEntityHelp(addBasicStudentParamsList, requestUser);
        this.saveOrUpdateBatch(basicStudentList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeBasicStudentBatch(List<Long> idList) {

        if (idList.size() == 0) {
            throw ExceptionResultEnum.ERROR.exception("请选择要删除的目标");
        }
        // 删除基础学生
        this.remove(new QueryWrapper<BasicStudent>().lambda().in(BasicStudent::getId, idList));
        // 同步删除教学学生
        this.baseMapper.deleteTeachStudentByBasicStudentIdSet(new HashSet<>(idList));
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeBasicStudentByCondition(String queryParams, Long collegeId, Long majorId, Set<Long> clazzIdList) {
        LambdaQueryWrapper<BasicStudent> basicStudentQueryWrapper = new QueryWrapper<BasicStudent>().lambda();
        if (SystemConstant.strNotNull(queryParams)) {
            basicStudentQueryWrapper.and(e -> e.like(BasicStudent::getStudentName, queryParams).or().like(BasicStudent::getStudentCode, queryParams));
        }
        if (SystemConstant.longNotNull(collegeId)) {
            basicStudentQueryWrapper.eq(BasicStudent::getBelongOrgId, collegeId);
        }
        if (SystemConstant.longNotNull(majorId)) {
            basicStudentQueryWrapper.eq(BasicStudent::getMajorId, majorId);
        }
        if (clazzIdList != null && clazzIdList.size() > 0) {
            basicStudentQueryWrapper.in(BasicStudent::getClazzId, clazzIdList);
        }
        List<BasicStudent> willDeleteStudent = this.list(basicStudentQueryWrapper);

        // 删除基础学生
        if (willDeleteStudent.size() > 0) {
            Set<Long> idSet = willDeleteStudent.stream().map(BaseEntity::getId).collect(Collectors.toSet());
            this.removeByIds(idSet);
            // 同步删除教学学生
            this.baseMapper.deleteTeachStudentByBasicStudentIdSet(idSet);
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> executeBasicStudentImportLogic(List<LinkedMultiValueMap<Integer, Object>> finalList, Map<String, Object> map) throws IllegalAccessException {
        SysUser sysUser = (SysUser) map.get(SystemConstant.SYS_USER);
        for (int i = 0; i < finalList.size(); i++) {
            LinkedMultiValueMap<Integer, Object> excelMap = finalList.get(i);
            List<Object> basicStudentImportDtoList = excelMap.get(i);

//            assert !basicStudentImportDtoList.isEmpty();
            if (basicStudentImportDtoList.isEmpty() || basicStudentImportDtoList.get(0) instanceof DescribeImportDto) {
                continue;
            }
            List<BasicStudentImportDto> datasource = basicStudentImportDtoList.stream().map(e -> {
                BasicStudentImportDto basicStudentImportDto = new BasicStudentImportDto();
                BeanUtils.copyProperties(e, basicStudentImportDto);
                return basicStudentImportDto;
            }).collect(Collectors.toList());

            List<BasicStudentExtrasParam> basicStudentExtrasParamList = datasource.stream().flatMap(e -> {
                BasicStudentExtrasParam basicStudentExtrasParam = new BasicStudentExtrasParam();
                basicStudentExtrasParam.setStudentName(e.getStudentName());
                basicStudentExtrasParam.setStudentCode(e.getStudentCode());
                basicStudentExtrasParam.setPhoneNumber(e.getPhoneNumber());
                basicStudentExtrasParam.setCollegeName(e.getCollegeName());
                basicStudentExtrasParam.setMajorName(e.getMajorName());
                basicStudentExtrasParam.setClazzName(e.getClazzName());
                return Stream.of(basicStudentExtrasParam);
            }).collect(Collectors.toList());
            this.saveBasicStudentWithExtrasList(basicStudentExtrasParamList, sysUser);
            map.put("dataCount", datasource.size());
        }
        return map;
    }

    @Override
    public void exportData(HttpServletResponse response, String queryParams, Long collegeId, Long majorId, Set<Long> clazzIdList) throws Exception {
        Long schoolId = SystemConstant.convertIdToLong(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        queryParams = SystemConstant.translateSpecificSign(queryParams);
        List<BasicStudentExportDto> studentExportDtos = basicStudentMapper.listExportData(queryParams, schoolId, collegeId, majorId, clazzIdList, dpr);
        if (studentExportDtos.isEmpty()) {
            throw ExceptionResultEnum.ERROR.exception("没有可导出数据");
        }
        ExcelUtil.excelExport("学生数据", BasicStudentExportDto.class, studentExportDtos, response);
    }

    /**
     * 学生保存助手方法（应对学生保存和导入批量保存）
     *
     * @param basicStudentParamsList 学生参数
     * @param sysUser                操作人
     * @return 结果
     */
    private List<BasicStudent> editEntityHelp(List<BasicStudentParams> basicStudentParamsList, SysUser sysUser) {

        Long schoolId = sysUser.getSchoolId();
        List<BasicStudent> editEntityList = new ArrayList<>();
        Map<String, BasicStudentParams> checkRepeatMap = new HashMap<>();
        for (BasicStudentParams basicStudentParams : basicStudentParamsList) {
            // 普通参数
            String studentName = basicStudentParams.getStudentName();
            String studentCode = basicStudentParams.getStudentCode();
            String phoneNumber = basicStudentParams.getPhoneNumber();
            Long clazzId = basicStudentParams.getClazzId();
            Long majorId = basicStudentParams.getMajorId();
            Long collegeId = basicStudentParams.getCollegeId();
            if (checkRepeatMap.containsKey(studentCode)) {
                // 校验整个导入批次是否有重复数据
                BasicStudentParams check = checkRepeatMap.get(studentCode);
                if (!Objects.equals(studentName, check.getStudentName())) {
                    throw ExceptionResultEnum.ERROR.exception("导入批次学号为【" + studentCode + "】的学生，存在不同姓名");
                }
                if (!Objects.equals(phoneNumber, check.getPhoneNumber())) {
                    throw ExceptionResultEnum.ERROR.exception("导入批次学号为【" + studentCode + "】的学生，存在不同电话");
                }
                if (!Objects.equals(clazzId, check.getClazzId())) {
                    throw ExceptionResultEnum.ERROR.exception("导入批次学号为【" + studentCode + "】的学生，存在不同班级");
                }
                if (!Objects.equals(majorId, check.getMajorId())) {
                    throw ExceptionResultEnum.ERROR.exception("导入批次学号为【" + studentCode + "】的学生，存在不同专业");
                }
                if (!Objects.equals(collegeId, check.getCollegeId())) {
                    throw ExceptionResultEnum.ERROR.exception("导入批次学号为【" + studentCode + "】的学生，存在不同学院");
                }
                continue;
            } else {
                checkRepeatMap.put(studentCode, basicStudentParams);
            }

            // 班级验证
            if (SystemConstant.longNotNull(clazzId)) {
                // 如果有班级id 再验证
                BasicClazz basicClazz = basicClazzService.getById(clazzId);
                if (Objects.isNull(basicClazz)) {
                    throw ExceptionResultEnum.ERROR.exception("所选班级不存在");
                }
                if (SystemConstant.longNotNull(majorId)) {
                    if (!majorId.equals(basicClazz.getMajorId())) {
                        String exception = "学生信息【" + studentName + "(" + studentCode + ")" + SystemConstant.HYPHEN + basicMajorService.getById(majorId).getName() + "】中的专业和学生所属班级信息【" + basicClazz.getClazzName() + "-" + basicMajorService.getById(basicClazz.getMajorId()).getName() + "】中的专业不对应";
                        throw ExceptionResultEnum.ERROR.exception(exception);
                    }
                }
            }


            // 专业验证
            if (SystemConstant.longNotNull(majorId)) {
                BasicMajor basicMajor = basicMajorService.getById(majorId);
                if (Objects.isNull(basicMajor)) {
                    throw ExceptionResultEnum.ERROR.exception("所选专业不存在");
                }
                if (SystemConstant.longNotNull(collegeId)) {
                    if (!collegeId.equals(basicMajor.getBelongOrgId())) {
                        String exception = "学生信息【" + studentName + "(" + studentCode + ")" + SystemConstant.HYPHEN + basicCollegeService.getById(collegeId).getCollegeName() +
                                "】中的学院和学生所属专业信息【" +
                                basicMajor.getName() + SystemConstant.HYPHEN + basicCollegeService.getById(basicMajor.getBelongOrgId()).getCollegeName() + "】中的学院不对应";
                        throw ExceptionResultEnum.ERROR.exception(exception);
                    }
                }
            }

            Long id = basicStudentParams.getId();
            // 检验学号在学校的唯一性
            BasicStudent checkCode = this.getOne(new QueryWrapper<BasicStudent>().lambda()
                    .eq(BasicStudent::getSchoolId, schoolId)
                    .eq(BasicStudent::getStudentCode, studentCode)
                    .eq(BasicStudent::getEnable, true));
            if (Objects.nonNull(checkCode)) {
                if (!checkCode.getId().equals(id)) {
                    throw ExceptionResultEnum.ERROR.exception("学生编号【" + studentCode + "】重复");
                }
            }

            // 检验有值的电话在学校的唯一性
            if (SystemConstant.strNotNull(phoneNumber)) {
                BasicStudent checkPhone = this.getOne(new QueryWrapper<BasicStudent>().lambda()
                        .eq(BasicStudent::getSchoolId, schoolId)
                        .eq(BasicStudent::getPhoneNumber, phoneNumber)
                        .eq(BasicStudent::getEnable, true));
                if (Objects.nonNull(checkPhone)) {
                    if (!checkPhone.getId().equals(id)) {
                        throw ExceptionResultEnum.ERROR.exception("学生电话【" + phoneNumber + "】重复");
                    }
                }
            }

            // 组装基础参数
            Long orgId;
            BasicStudent basicStudent = new BasicStudent();
            basicStudent.setStudentCode(studentCode);
            basicStudent.setStudentName(studentName);
            basicStudent.setPhoneNumber(phoneNumber);
            basicStudent.setBelongOrgId(collegeId);
            basicStudent.setMajorId(majorId);
            basicStudent.setClazzId(clazzId);
            basicStudent.setSchoolId(schoolId);

            // 保存
            if (!SystemConstant.longNotNull(id)) {
                // id为空 -> 新增
                // 获取分布式id
                orgId = sysUser.getOrgId();
                id = SystemConstant.getDbUuid();
                basicStudent.setId(id);

                basicStudent.setCreateId(sysUser.getId());
            } else {
                // id不为空 -> 更新
                BasicStudent old = this.getById(id);
                if (Objects.isNull(old) || old.getEnable().equals(false)) {
                    throw ExceptionResultEnum.ERROR.exception("学生主键不存在或被禁用");
                }
                orgId = old.getOrgId();
                basicStudent.setId(id);
                basicStudent.setUpdateId(sysUser.getId());
            }
            basicStudent.setOrgId(orgId);
            editEntityList.add(basicStudent);
        }
        return editEntityList;
    }
}
