package com.qmth.distributed.print.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.distributed.print.business.bean.dto.ExamTaskDetailDto;
import com.qmth.distributed.print.business.bean.dto.ExamTaskDetailPdfDownloadDto;
import com.qmth.distributed.print.business.bean.params.RelatePaperParam;
import com.qmth.distributed.print.business.cache.CreatePdfCacheUtil;
import com.qmth.distributed.print.business.entity.*;
import com.qmth.distributed.print.business.enums.*;
import com.qmth.distributed.print.business.mapper.ExamTaskDetailMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysOrg;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.*;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.ExamTaskUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import com.qmth.teachcloud.mark.service.MarkService;
import com.qmth.teachcloud.mark.service.ScanPaperService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 命题任务明细表（入库申请） 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class ExamTaskDetailServiceImpl extends ServiceImpl<ExamTaskDetailMapper, ExamTaskDetail> implements ExamTaskDetailService {

    @Resource
    private ExamTaskService examTaskService;
    @Resource
    private ExamCardService examCardService;
    @Resource
    private ExamDetailService examDetailService;
    @Resource
    private ExamDetailCourseService examDetailCourseService;
    @Resource
    private ExamStudentService examStudentService;
    @Resource
    private ExamPrintPlanService examPrintPlanService;
    @Resource
    private TBTaskPdfService tbTaskPdfService;
    @Resource
    private TBSyncTaskService tbSyncTaskService;
    @Resource
    private ActivitiService activitiService;
    @Resource
    private TaskService taskService;
    @Resource
    private TFFlowApproveService tfFlowApproveService;
    @Resource
    private TFFlowJoinService tfFlowJoinService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private CommonCacheService commonCacheService;
    @Resource
    private ExamTaskDetailMapper examTaskDetailMapper;
    @Resource
    private ScanPaperService scanPaperService;
    @Resource
    private BasicRoleDataPermissionService basicRoleDataPermissionService;
    @Resource
    private MarkService markService;
    @Resource
    private ExamTaskAssignPaperTypeService examTaskAssignPaperTypeService;

    @Transactional
    @Override
    public boolean enable(ExamTaskDetail examTaskDetail) {
        ExamTask examTask = examTaskService.getById(examTaskDetail.getId());
        List<ExamDetail> examDetails = examDetailService.listByCourseCodeAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber());
        if (examDetails != null && examDetails.size() > 0) {
            // 有考场提交打印，不能禁用启用
            long count = examDetails.stream().filter(m -> !ExamDetailStatusEnum.NEW.name().equals(m.getStatus().name()) && !ExamDetailStatusEnum.READY.name().equals(m.getStatus().name())).count();
            if (count > 0) {
                throw ExceptionResultEnum.ERROR.exception("有考场已提交打印，不能启用/禁用");
            }

            Map<Long, List<Long>> map = examDetails.stream().collect(Collectors.groupingBy(ExamDetail::getPrintPlanId, Collectors.mapping(ExamDetail::getId, Collectors.toList())));
            for (Map.Entry<Long, List<Long>> listEntry : map.entrySet()) {
                int countRunning = tbTaskPdfService.countRunningByExamDetailIds(listEntry.getValue());
                if (countRunning > 0) {
                    throw ExceptionResultEnum.ERROR.exception("有关联该任务的考场正在生成打印pdf，不能启用/禁用卷库");
                }
            }
        }
        // 禁用，解决试卷关联关系，并且解除生成pdf绑定
        if (!examTaskDetail.getEnable()) {
            // 正在生成pdf不能禁用
            if (examDetails != null && examDetails.size() > 0) {
                Map<Long, List<Long>> map = examDetails.stream().collect(Collectors.groupingBy(ExamDetail::getPrintPlanId, Collectors.mapping(ExamDetail::getId, Collectors.toList())));
                for (Map.Entry<Long, List<Long>> listEntry : map.entrySet()) {
                    // 解除所有考场该任务绑定关系
                    // 更新exam_detail
                    List<Long> examDetailIds = listEntry.getValue();
                    UpdateWrapper<ExamDetail> examDetailUpdateWrapper = new UpdateWrapper<>();
                    examDetailUpdateWrapper.lambda()
                            .set(ExamDetail::getStatus, ExamDetailStatusEnum.NEW)
                            .set(ExamDetail::getAttachmentId, null)
                            .set(ExamDetail::getAttachmentPath, null)
                            .set(ExamDetail::getPaperPages, null)
                            .set(ExamDetail::getCardPages, null)
                            .set(ExamDetail::getPagesA4, null)
                            .in(ExamDetail::getId, examDetailIds);
                    examDetailService.update(examDetailUpdateWrapper);
                    // 更新绑定关系
                    List<ExamDetailCourse> examDetailCourses = examDetailCourseService.listByPrintPlanIdAndCourseCodeAndPaperNumber(examTask.getSchoolId(), listEntry.getKey(), examTask.getCourseCode(), examTask.getPaperNumber());
                    List<Long> examDetailCourseIds = examDetailCourses.stream().map(m -> m.getId()).collect(Collectors.toList());
                    UpdateWrapper<ExamDetailCourse> examDetailCourseUpdateWrapper = new UpdateWrapper<>();
                    examDetailCourseUpdateWrapper.lambda()
                            .set(ExamDetailCourse::getPaperType, null)
                            .set(ExamDetailCourse::getPaperPagesA3, null)
                            .set(ExamDetailCourse::getCardPagesA3, null)
                            .in(ExamDetailCourse::getId, examDetailCourseIds);
                    examDetailCourseService.update(examDetailCourseUpdateWrapper);

                    // 全部任务都取消，更新计划状态
                    //所有考场都撤回，印刷任务状态改为就绪
                    examPrintPlanService.updateStatusById(listEntry.getKey(), PrintPlanStatusEnum.READY);
                }
            }
        }
        UpdateWrapper<ExamTaskDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamTaskDetail::getEnable, examTaskDetail.getEnable()).eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getId());
        this.update(updateWrapper);

        return true;
    }

    @Transactional
    @Override
    public boolean updatePaper(RelatePaperParam paperParam) {
        ExamTask examTask = examTaskService.getById(paperParam.getExamTaskId());
        // 提交印刷的考场，不允许修改关联试卷
        List<ExamDetail> examDetails = examDetailService.listByPrintPlanIdAndCourseCodeAndPaperNumber(examTask.getSchoolId(), paperParam.getPrintPlanId(), examTask.getCourseCode(), examTask.getPaperNumber());
        List<ExamDetail> examDetailsPrint = examDetails.stream().filter(m -> !ExamDetailStatusEnum.NEW.equals(m.getStatus()) && !ExamDetailStatusEnum.READY.equals(m.getStatus())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(examDetailsPrint)) {
            throw ExceptionResultEnum.ERROR.exception("部分考场pdf文件已提交印刷或已印刷，不能修改卷型");
        }

        List<Long> examDetailIds = examDetails.stream().map(ExamDetail::getId).collect(Collectors.toList());
        int countRunning = tbTaskPdfService.countRunningByExamDetailIds(examDetailIds);
        if (countRunning > 0) {
            throw ExceptionResultEnum.ERROR.exception("部分考场pdf文件正在生成中，不能修改卷型");
        }

        for (ExamDetail examDetail : examDetails) {
            List<ExamDetailCourse> examDetailCourses = examDetailCourseService.listByExamDetailIdAndStatus(examDetail.getId());
            // 4.没有未完成的命题任务
            if (CollectionUtils.isNotEmpty(examDetailCourses)) {
                throw ExceptionResultEnum.ERROR.exception("部分考场包含的命题任务未完成，不能修改卷型");
            }
        }

        // 更新命题任务中关联卷型
        ExamTask newExamTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(examTask.getExamId(), examTask.getCourseCode(), paperParam.getPaperNumber());
        UpdateWrapper<ExamTaskDetail> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(ExamTaskDetail::getRelatePaperType, paperParam.getRelatePaperType()).eq(ExamTaskDetail::getExamTaskId, newExamTask.getId());
        this.update(updateWrapper);

        // 更新选中的试卷编号、卷型
        String examDetailCourseIdStr = paperParam.getExamDetailCourseIds();
        List<Long> examDetailCourseIds = Arrays.asList(examDetailCourseIdStr.split(",")).stream().map(m -> Long.valueOf(m)).collect(Collectors.toList());
        examDetailCourseService.updatePaperNumber(examDetailCourseIds, paperParam.getPaperNumber(), paperParam.getRelatePaperType());

        // 更新抽取卷型数据
        for (ExamDetail examDetail : examDetails) {
            ExamTaskAssignPaperType examTaskAssignPaperType = new ExamTaskAssignPaperType(paperParam.getExamId(), paperParam.getPaperNumber(), examDetail.getExamStartTime(), examDetail.getExamEndTime(), paperParam.getRelatePaperType(), AssignModeEnum.MANUAL);
            examTaskAssignPaperTypeService.saveOrUpdateByMultiId(examTaskAssignPaperType);
        }

        // 更新考生关联类型
        ExamTaskDetail examTaskDetail = this.getByExamTaskId(newExamTask.getId());
        List<String> types = Arrays.asList(paperParam.getRelatePaperType().split(","));
        int drawCount = examTaskDetail.getDrawCount().intValue();
        if (drawCount - types.size() != 0) {
            throw ExceptionResultEnum.ERROR.exception("关联卷型与设置数量不一致");
        }

        // 初始化PDF生成任务数据
        for (ExamDetail examDetail : examDetails) {
            tbTaskPdfService.resetRunningCreatePdf(examDetail.getId(), CreatePdfTypeEnum.ALL);
        }
        return true;
    }

    @Override
    public List<ExamTaskDetailDto> list(Long semesterId, Long examId, String relateType, List<Long> printPlanIdList, String courseCode, String paperNumber, String userName, Long cardRuleId) {
        userName = SystemConstant.translateSpecificSign(userName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        List<ExamTaskDetailDto> examDetailDtoIPage = this.baseMapper.listPage(schoolId, semesterId, examId, relateType, printPlanIdList, courseCode, paperNumber, userName, cardRuleId, dpr);
        examDetailDtoIPage.stream().map(m -> {
            String paperType = StringUtils.isBlank(m.getRelatePaperType()) ? null : m.getRelatePaperType();
            Long printPlanIdT = Long.valueOf(m.getPrintPlanId());
            String courseCodeT = m.getCourseCode();
            String paperNumberT = m.getPaperNumber();
            List<String> examDetailCourseIds = this.baseMapper.listExamDetailCourse(schoolId, paperType, printPlanIdT, courseCodeT, paperNumberT);
            m.setExamDetailCourseIds(String.join(",", examDetailCourseIds));
            return m;
        }).collect(Collectors.toList());
        return examDetailDtoIPage;
    }

    @Override
    public Map listTotal(Long semesterId, Long examId, String relateType, List<Long> printPlanIdList, String courseCode, String paperNumber, String userName, Long cardRuleId) {
        userName = SystemConstant.translateSpecificSign(userName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        List<ExamTaskDetailDto> examDetailDtoList = this.baseMapper.listPage(schoolId, semesterId, examId, relateType, printPlanIdList, courseCode, paperNumber, userName, cardRuleId, dpr);
        Map<String, Object> map = new HashMap<>();
        // 课程数量
        long courseCodeCount = examDetailDtoList.stream().map(m -> m.getCourseCode()).distinct().count();
        map.put("courseTotal", courseCodeCount);
        // 命题任务数量
        long paperNumberCount = examDetailDtoList.stream().map(m -> m.getPaperNumber()).distinct().count();
        map.put("taskTotal", paperNumberCount);
        // 科次数量
        int subjectTotalCount = examDetailDtoList.stream().mapToInt(m -> m.getTotalSubjects()).sum();
        map.put("subjectTotal", subjectTotalCount);
        return map;
    }

    @Override
    public void resetCardId(Long id) {
        QueryWrapper<ExamTaskDetail> examTaskDetailQueryWrapper = new QueryWrapper<>();
        examTaskDetailQueryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, id);
        List<ExamTaskDetail> examTaskDetails = this.list(examTaskDetailQueryWrapper);
        if (CollectionUtils.isNotEmpty(examTaskDetails)) {
            for (ExamTaskDetail e : examTaskDetails) {
                if (Objects.nonNull(e.getPaperAttachmentIds())) {
                    List<PaperInfoVo> paperInfoVoList = ExamTaskUtil.parsePaperAttachmentPath(e.getPaperAttachmentIds());
                    for (PaperInfoVo paperInfoVo : paperInfoVoList) {
                        paperInfoVo.setCardId(null);
                    }
                    e.setPaperAttachmentIds(JSON.toJSONString(paperInfoVoList));
                }
            }
            this.updateBatchById(examTaskDetails);
        }
    }

    @Override
    @Transactional
    public Map<String, Object> paperUpdate(ExamTaskDetail examTaskDetail) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();

        validPaperUpdate(examTaskDetail);
        SysOrg sysOrg = commonCacheService.orgCache(requestUser.getOrgId());

        Map<String, Object> mapVar = new HashMap<>();
        ExamTaskDetail examTaskDetail1 = this.getByExamTaskId(examTaskDetail.getExamTaskId());
        String unexposedPaperType = examTaskDetail.getPaperType();
        if (StringUtils.isNotBlank(examTaskDetail1.getExposedPaperType())) {
            List<String> paperTypeAll = new ArrayList<>(Arrays.asList(examTaskDetail.getPaperType().split(",")));
            List<String> exposePaperTypes = Arrays.asList(examTaskDetail1.getExposedPaperType().split(","));
            for (String exposePaperType : exposePaperTypes) {
                if (paperTypeAll.contains(exposePaperType)) {
                    paperTypeAll.remove(exposePaperType);
                }
            }
            unexposedPaperType = String.join(",", paperTypeAll);
        }

        UpdateWrapper<ExamTaskDetail> examTaskDetailUpdateWrapper = new UpdateWrapper<>();
        examTaskDetailUpdateWrapper.lambda().set(ExamTaskDetail::getPaperType, examTaskDetail.getPaperType())
                .set(ExamTaskDetail::getPaperAttachmentIds, examTaskDetail.getPaperAttachmentIds())
                .set(ExamTaskDetail::getUnexposedPaperType, unexposedPaperType)
                .set(ExamTaskDetail::getRelatePaperType, null)
                .set(ExamTaskDetail::getDrawCount, examTaskDetail.getDrawCount())
                .eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getExamTaskId());
        this.update(examTaskDetailUpdateWrapper);


        QueryWrapper<ExamTaskDetail> examTaskDetailQueryWrapper = new QueryWrapper<>();
        examTaskDetailQueryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getExamTaskId());
        List<ExamTaskDetail> examTaskDetailList = this.list(examTaskDetailQueryWrapper);
        if (CollectionUtils.isNotEmpty(examTaskDetailList)) {
            for (ExamTaskDetail e : examTaskDetailList) {
                e.setPaperType(examTaskDetail.getPaperType());
                e.setPaperAttachmentIds(examTaskDetail.getPaperAttachmentIds());
                e.setUnexposedPaperType(unexposedPaperType);
            }
        }

        ExamTask examTask = examTaskService.getById(examTaskDetail.getExamTaskId());
        List<ExamDetail> examDetails = examDetailService.listByCourseCodeAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber());
        if (CollectionUtils.isNotEmpty(examDetails)) {
            StringJoiner stringJoiner = new StringJoiner("_");
            stringJoiner.add(String.valueOf(examDetails.get(0).getSchoolId()))
                    .add(String.valueOf(examDetails.get(0).getExamStartTime()))
                    .add(String.valueOf(examDetails.get(0).getExamEndTime()))
                    .add(examTask.getCourseCode())
                    .add(examTask.getPaperNumber());
            String key = stringJoiner.toString();
            CreatePdfCacheUtil.deletePaperType(key);

            List<ExamDetail> examDetailList = examDetails.stream().filter(m -> m.getStatus().equals(ExamDetailStatusEnum.WAITING)).collect(Collectors.toList());
            for (ExamDetail examDetail : examDetailList) {
                examDetail.setStatus(ExamDetailStatusEnum.NEW);
                examDetailService.updateById(examDetail);
            }
        }

        // 不需要审核，直接更新，否则加入待审核列表(状态为不需要审核的或者为管理员身份的都不重新走审核流程)
        if ((Objects.nonNull(examTask.getReview()) && examTask.getReview()) || !sysUserService.isAdminDataAuth(requestUser.getId())) {
            // 发起新流程
            Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                    examTask,
                    TFCustomTypeEnum.ELECTRON_FLOW,
                    examTask.getUserId(),
                    false);
            map.computeIfAbsent(SystemConstant.OLD_FLOW_ID, v -> examTask.getFlowId());
            map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.RESTART);
            map = activitiService.customFlowStart(map);
            Long flowId = SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID));
            UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamTask::getFlowId, flowId).eq(ExamTask::getId, examTaskDetail.getExamTaskId());
            examTaskService.update(updateWrapper);
            mapVar.putAll(map);
            mapVar.put(SystemConstant.SEND_FLOW_START_MQ, true);

            tfFlowJoinService.saveOrUpdate(map);
            if (Objects.nonNull(flowId)) {
                // 审核一级
                Task task = taskService.createTaskQuery().processInstanceId(String.valueOf(flowId)).singleResult();
                if (Objects.nonNull(task)) {
                    Map<String, Object> approverMap = new HashMap<>();
                    approverMap.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> task.getId());
                    approverMap.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
                    approverMap.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
                    String remark = Objects.nonNull(sysOrg) ? sysOrg.getName() : "无机构";
                    approverMap.computeIfAbsent(SystemConstant.APPROVE_REMARK, v -> requestUser.getRealName() + "(" + remark + ")" + FlowApprovePassEnum.RESTART.getTitle());
                    activitiService.taskApprove(approverMap);
                    mapVar.putAll(approverMap);
                    mapVar.remove(SystemConstant.SEND_FLOW_START_MQ);
                    mapVar.put(SystemConstant.SEND_FLOW_MQ, true);
                }
            }
        }
        mapVar.put(SystemConstant.SUCCESS, true);
        return mapVar;
    }

    /**
     * 重启流程
     *
     * @param examTaskDetail
     * @return
     */
    @Override
    public Map<String, Object> taskRestart(ExamTaskDetail examTaskDetail) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        SysOrg sysOrg = commonCacheService.orgCache(requestUser.getOrgId());

        Map<String, Object> mapVar = new HashMap<>();
        ExamTask examTask = examTaskService.getById(examTaskDetail.getExamTaskId());
        List<ExamDetail> examDetailList = examDetailService.listByCourseCodeAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber());
        if (CollectionUtils.isNotEmpty(examDetailList)) {
            ExamTaskDetail taskDetail = this.getByExamTaskId(examTaskDetail.getExamTaskId());
            List<Long> examDetailIds = examDetailList.stream().map(ExamDetail::getId).collect(Collectors.toList());
            QueryWrapper<ExamDetailCourse> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().in(ExamDetailCourse::getExamDetailId, examDetailIds);
            List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(queryWrapper);
            Set<String> paperTypeSet = new HashSet<>();
            for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
                // 考场内任一课程有扫描数据，不允许打回
                boolean isScanPaper = scanPaperService.isScanByExamIdAndPaperNumber(examTask.getExamId(), examDetailCourse.getPaperNumber());
                if (isScanPaper) {
                    throw ExceptionResultEnum.ERROR.exception("有课程已开始扫描，无法打回");
                }
                if (StringUtils.isNotBlank(examDetailCourse.getPaperType())) {
                    paperTypeSet.addAll(Arrays.asList(examDetailCourse.getPaperType().split(",")));
                }
            }

            // 已打印任务，必须先作废，未打印任务，必须先撤回。才允许打回
            long count = examDetailList.stream().filter(m -> ExamDetailStatusEnum.FINISH.equals(m.getStatus()) || ExamDetailStatusEnum.WAITING.equals(m.getStatus())).count();
            if (count > 0) {
                throw ExceptionResultEnum.ERROR.exception("印刷任务有已打印或待印刷数据，无法打回");
            }

            // 修改已曝光和未曝光类型
            List<String> exposedPaperTypeList = StringUtils.isNotBlank(taskDetail.getExposedPaperType()) ? new ArrayList<>(Arrays.asList(taskDetail.getExposedPaperType().split(","))) : new ArrayList<>();
            List<String> unexposedPaperTypeList = StringUtils.isNotBlank(taskDetail.getUnexposedPaperType()) ? new ArrayList<>(Arrays.asList(taskDetail.getUnexposedPaperType().split(","))) : new ArrayList<>();
            for (String exposePaperType : paperTypeSet) {
                if (exposedPaperTypeList.contains(exposePaperType)) {
                    exposedPaperTypeList.remove(exposePaperType);
                    unexposedPaperTypeList.add(exposePaperType);
                }
            }
            exposedPaperTypeList.sort(Comparator.comparing(String::toString));
            unexposedPaperTypeList.sort(Comparator.comparing(String::toString));
            taskDetail.setExposedPaperType(CollectionUtils.isEmpty(exposedPaperTypeList) ? null : String.join(",", exposedPaperTypeList));
            taskDetail.setUnexposedPaperType(String.join(",", unexposedPaperTypeList));
            taskDetail.setRelatePaperType(null);
            this.updateById(taskDetail);

            StringJoiner stringJoiner = new StringJoiner("_");
            stringJoiner.add(String.valueOf(examTask.getExamId())).add(examTask.getPaperNumber()).add(String.valueOf(examDetailList.get(0).getExamStartTime())).add(String.valueOf(examDetailList.get(0).getExamEndTime()));
            String key = stringJoiner.toString();
            CreatePdfCacheUtil.deletePaperType(key);
            for (ExamDetail examDetail : examDetailList) {
                examDetailService.resetExamDetail(examDetail.getId(), CreatePdfTypeEnum.ALL, ExamDetailStatusEnum.REJECT, true);
            }
        }

        // 不需要审核，直接更新，否则加入待审核列表(状态为不需要审核的或者为管理员身份的都不重新走审核流程)
        if ((Objects.nonNull(examTask.getReview()) && examTask.getReview())) {
            // 发起新流程
            Map<String, Object> map = SystemConstant.buildFlowVar(examTask.getId(),
                    examTask,
                    TFCustomTypeEnum.ELECTRON_FLOW,
                    examTask.getUserId(),
                    false);
            map.computeIfAbsent(SystemConstant.OLD_FLOW_ID, v -> examTask.getFlowId());
            map.computeIfAbsent(SystemConstant.FLOW_JOIN_TYPE, v -> FlowJoinTypeEnum.RESTART);
            String remark = Objects.nonNull(sysOrg) ? sysOrg.getName() : "无机构";
            map.computeIfAbsent(SystemConstant.APPROVE_REMARK, v -> requestUser.getRealName() + "(" + remark + ")" + FlowApprovePassEnum.RESTART.getTitle());
            map = activitiService.customFlowStart(map);
            Long flowId = SystemConstant.convertIdToLong((String) map.get(SystemConstant.FLOW_ID));
            UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamTask::getFlowId, flowId).eq(ExamTask::getId, examTaskDetail.getExamTaskId());
            examTaskService.update(updateWrapper);
            mapVar.putAll(map);
            mapVar.put(SystemConstant.SEND_FLOW_START_MQ, true);

            tfFlowJoinService.saveOrUpdate(map);
//            if (Objects.nonNull(flowId)) {
//                // 审核一级
//                Task task = taskService.createTaskQuery().processInstanceId(String.valueOf(flowId)).singleResult();
//                if (Objects.nonNull(task)) {
//                    Map<String, Object> approverMap = new HashMap<>();
//                    approverMap.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> task.getId());
//                    approverMap.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
//                    approverMap.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.PASS);
////                    map.computeIfAbsent(SystemConstant.APPROVE_USER_IDS, v -> Arrays.asList(examTask.getUserId()));
//                    String remark = Objects.nonNull(sysOrg) ? sysOrg.getName() : "无机构";
//                    approverMap.computeIfAbsent(SystemConstant.APPROVE_REMARK, v -> requestUser.getRealName() + "(" + remark + ")" + FlowApprovePassEnum.RESTART.getTitle());
//                    activitiService.taskApprove(approverMap);
//
//                    try {
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    Task taskReject = taskService.createTaskQuery().processInstanceId(String.valueOf(flowId)).singleResult();
//                    Map<String, Object> approverRejectMap = new HashMap<>();
//                    approverRejectMap.computeIfAbsent(SystemConstant.FLOW_TASK_ID, v -> taskReject.getId());
//                    approverRejectMap.computeIfAbsent(SystemConstant.PAPAER_ATTACHMENT_IDS, v -> examTaskDetail.getPaperAttachmentIds());
//                    approverRejectMap.computeIfAbsent(SystemConstant.APPROVE_OPERATION, v -> FlowApprovePassEnum.REJECT);
//                    approverRejectMap.computeIfAbsent(SystemConstant.APPROVE_SETUP, v -> FlowApproveSetupEnum.SUBMIT.getSetup());
//                    activitiService.taskApprove(approverRejectMap);
//                    mapVar.putAll(approverMap);
//                    mapVar.remove(SystemConstant.SEND_FLOW_START_MQ);
//                    mapVar.put(SystemConstant.SEND_FLOW_MQ, true);
//                }
//            }
        } else {
            // 不需要审核
            UpdateWrapper<ExamTask> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamTask::getStatus, ExamStatusEnum.STAGE).eq(ExamTask::getId, examTaskDetail.getExamTaskId());
            examTaskService.update(updateWrapper);
        }

        // 删除阅卷数据
        markService.deleteInitMarkData(examTask.getExamId(), examTask.getPaperNumber());

        mapVar.put(SystemConstant.SUCCESS, true);
        return mapVar;
    }

    @Override
    public ExamTaskDetail getByExamTaskIdNotValid(Long examTaskId) {
        ExamTask examTask = examTaskService.getById(examTaskId);
        if (examTask == null) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不存在");
        }
        QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskId);
        return this.getOne(queryWrapper);
    }

    @Override
    public ExamTaskDetail getByExamTaskId(Long examTaskId) {
        ExamTask examTask = examTaskService.getById(examTaskId);
        if (examTask == null) {
            throw ExceptionResultEnum.ERROR.exception("命题任务不存在");
        }
        QueryWrapper<ExamTaskDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTaskId);
        ExamTaskDetail examTaskDetail = this.getOne(queryWrapper);
        if (examTaskDetail == null) {
            throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的命题任务未找到命题详情", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
        } else if (!examTaskDetail.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception(String.format("课程[%s（%s）]，试卷编号[%s]的命题任务卷库已禁用", examTask.getCourseName(), examTask.getCourseCode(), examTask.getPaperNumber()));
        }
        return examTaskDetail;
    }

    /**
     * 根据card查找json
     *
     * @param cardId
     * @return
     */
    @Override
    public List<ExamTaskDetail> getExamTaskDetailByCardIdJson(Long cardId) {
        return examTaskDetailMapper.getExamTaskDetailByCardIdJson(cardId);
    }

    /**
     * 根据examTaskId下载pdf
     *
     * @param examTaskId
     * @return
     */
    @Override
    public ExamTaskDetailPdfDownloadDto findPdfDownload(Long examTaskId) {
        return examTaskDetailMapper.findPdfDownload(examTaskId);
    }

    @Override
    public List<String> listRelateCommonTypes(Long semesterId, Long examId, String relateType, List<Long> idList, String courseCode, String paperNumber, String userName, Long cardRuleId) {
        userName = SystemConstant.translateSpecificSign(userName);
        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(schoolId, requestUser.getId(), ServletUtil.getRequest().getServletPath());
        List<ExamTaskDetailDto> examDetailDtoList = this.baseMapper.listPage(schoolId, semesterId, examId, relateType, idList, courseCode, paperNumber, userName, cardRuleId, dpr);
        List<String> commonPaperTypes = new ArrayList<>();
        if (!examDetailDtoList.isEmpty()) {
            // 所有未曝光类型不为空的数据
            List<String> unposedPaperList = examDetailDtoList.stream().filter(m -> StringUtils.isNotBlank(m.getUnexposedPaperType())).map(m -> m.getUnexposedPaperType()).collect(Collectors.toList());
            if (examDetailDtoList.size() == unposedPaperList.size()) {
                // 取所有命题任务未曝光类型的交集
                List<String> basePaperTypes = Arrays.asList(unposedPaperList.get(0).split(","));
                for (String basePaperType : basePaperTypes) {
                    for (String s : unposedPaperList) {
                        List<String> particPaperTypes = Arrays.asList(s.split(","));
                        if (!particPaperTypes.contains(basePaperType)) {
                            break;
                        }
                    }
                    commonPaperTypes.add(basePaperType);
                }
            }
        }
        return commonPaperTypes;
    }

    @Override
    public ExamTaskDetail findByExamIdAndCourseCodeAndPaperNumber(Long examId, String code, String number) {
        ExamTask examTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(examId, code, number);
        if (examTask == null) {
            return null;
        }
        return this.getByExamTaskId(examTask.getId());
    }

    @Transactional
    @Override
    public boolean cancel(Long id, String cancelRemark) {
        ExamTask examTask = examTaskService.getById(id);
        String paperNumber = examTask.getPaperNumber();

        List<ExamDetail> examDetailList = examDetailService.listByCourseCodeAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber());
        if (CollectionUtils.isNotEmpty(examDetailList)) {
            ExamTaskDetail taskDetail = this.getByExamTaskId(id);
            if (StringUtils.isNotBlank(taskDetail.getExposedPaperType())) {
                List<Long> examDetailIds = examDetailList.stream().map(ExamDetail::getId).collect(Collectors.toList());
                QueryWrapper<ExamDetailCourse> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().in(ExamDetailCourse::getExamDetailId, examDetailIds);
                List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.list(queryWrapper);
                for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
                    // 考场内任一课程有扫描数据，不允许打回
                    boolean isScanPaper = scanPaperService.isScanByExamIdAndPaperNumber(examTask.getExamId(), examDetailCourse.getPaperNumber());
                    if (isScanPaper) {
                        throw ExceptionResultEnum.ERROR.exception("有课程已开始扫描，无法打回");
                    }
                }

                // 已打印任务，必须先作废，未打印任务，必须先撤回。才允许打回
                long count = examDetailList.stream().filter(m -> (ExamDetailStatusEnum.FINISH.equals(m.getStatus()) && m.getNormal()) || ExamDetailStatusEnum.WAITING.equals(m.getStatus())).count();
                if (count > 0) {
                    throw ExceptionResultEnum.ERROR.exception("印刷任务有已打印且未作废或者未撤回数据，无法打回");
                }
            }

            StringJoiner stringJoiner = new StringJoiner("_");
            stringJoiner.add(String.valueOf(examTask.getExamId())).add(examTask.getPaperNumber()).add(String.valueOf(examDetailList.get(0).getExamStartTime())).add(String.valueOf(examDetailList.get(0).getExamEndTime()));
            String key = stringJoiner.toString();
            CreatePdfCacheUtil.deletePaperType(key);
            for (ExamDetail examDetail : examDetailList) {
                examDetailService.resetExamDetail(examDetail.getId(), CreatePdfTypeEnum.ALL, ExamDetailStatusEnum.REJECT, true);
            }
        }

        String paperNumberCancel = paperNumber + "-作废";
        examTask.setPaperNumber(paperNumberCancel);
        examTask.setStatus(ExamStatusEnum.CANCEL);
        examTask.setCancelRemark(cancelRemark);
        examTaskService.updateById(examTask);

        // 作废后，考场状态改为作废，考务数据保留
        List<ExamDetailCourse> examDetailCourseList = examDetailCourseService.listExamDetailByExamIdAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), paperNumber);
        // 考场状态改为作废
        if (CollectionUtils.isNotEmpty(examDetailCourseList)) {
            for (ExamDetailCourse examDetailCourse : examDetailCourseList) {
                UpdateWrapper<ExamDetail> examDetailUpdateWrapper = new UpdateWrapper<>();
                examDetailUpdateWrapper.lambda().set(ExamDetail::getStatus, ExamDetailStatusEnum.CANCEL)
                        .set(ExamDetail::getNormal, false)
                        .eq(ExamDetail::getId, examDetailCourse.getExamDetailId());
                examDetailService.update(examDetailUpdateWrapper);

                UpdateWrapper<ExamDetailCourse> examDetailCourseUpdateWrapper = new UpdateWrapper<>();
                examDetailCourseUpdateWrapper.lambda().set(ExamDetailCourse::getPaperNumber, paperNumberCancel)
                        .eq(ExamDetailCourse::getId, examDetailCourse.getId());
                examDetailCourseService.update(examDetailCourseUpdateWrapper);
            }
        }

        // 任务数据改为作废
        UpdateWrapper<TBTaskPdf> tbTaskUpdateWrapper = new UpdateWrapper<>();
        tbTaskUpdateWrapper.lambda().set(TBTaskPdf::getPaperNumber, paperNumberCancel)
                .eq(TBTaskPdf::getSchoolId, examTask.getSchoolId())
                .eq(TBTaskPdf::getExamId, examTask.getExamId())
                .eq(TBTaskPdf::getPaperNumber, paperNumber);
        tbTaskPdfService.update(tbTaskUpdateWrapper);

        // 考生表数据改为作废
        UpdateWrapper<ExamStudent> examStudentUpdateWrapper = new UpdateWrapper<>();
        examStudentUpdateWrapper.lambda().set(ExamStudent::getPaperNumber, paperNumberCancel)
                .eq(ExamStudent::getSchoolId, examTask.getSchoolId())
                .eq(ExamStudent::getExamId, examTask.getExamId())
                .eq(ExamStudent::getPaperNumber, paperNumber);
        examStudentService.update(examStudentUpdateWrapper);

        // 同步表数据改为作废
        UpdateWrapper<TBSyncTask> tbSyncTaskUpdateWrapper = new UpdateWrapper<>();
        tbSyncTaskUpdateWrapper.lambda().set(TBSyncTask::getPaperNumber, paperNumberCancel)
                .eq(TBSyncTask::getSchoolId, examTask.getSchoolId())
                .eq(TBSyncTask::getExamId, examTask.getExamId())
                .eq(TBSyncTask::getPaperNumber, paperNumber);
        tbSyncTaskService.update(tbSyncTaskUpdateWrapper);

        // 删除阅卷数据
        markService.deleteInitMarkData(examTask.getExamId(), examTask.getPaperNumber());

        return true;
    }

    @Override
    public ExamTaskDetail getByExamIdAndCourseCodeAndPaperNumber(Long examId, String courseCode, String paperNumber) {
        ExamTask examTask = examTaskService.getByExamIdAndCourseCodeAndPaperNumber(examId, courseCode, paperNumber);
        if (examTask == null) {
            throw ExceptionResultEnum.ERROR.exception("未查询到试卷信息");
        }
        return this.getByExamTaskId(examTask.getId());
    }

    @Override
    public void updateExposePaperType(Long examId, List<ExamDetailCourse> examDetailCourses) {
        // 更新曝光卷型、未曝光卷型
        for (ExamDetailCourse examDetailCourse : examDetailCourses) {
            ExamTaskDetail examTaskDetail = this.getByExamIdAndCourseCodeAndPaperNumber(examId, examDetailCourse.getCourseCode(), examDetailCourse.getPaperNumber());
            // 考生使用卷型
            List<String> studentPaperTypes = examStudentService.listPaperTypeByExamDetailCourseId(String.valueOf(examDetailCourse.getId()));
            List<String> paperTypeList = Objects.isNull(examTaskDetail.getPaperType()) ? new ArrayList<>() : Arrays.asList(examTaskDetail.getPaperType().split(","));
            List<String> exposedPaperTypeList = Objects.isNull(examTaskDetail.getExposedPaperType()) ? new ArrayList<>() : Arrays.asList(examTaskDetail.getExposedPaperType().split(","));
            // 曝光卷型
            List<String> unionList = (List<String>) CollectionUtils.union(exposedPaperTypeList, studentPaperTypes);
            // 未曝光卷型
            List<String> subtractList = (List<String>) CollectionUtils.subtract(paperTypeList, unionList);
            UpdateWrapper<ExamTaskDetail> examTaskDetailUpdateWrapper = new UpdateWrapper<>();
            String exposedPaper = String.join(",", unionList);
            String unexposedPaper = String.join(",", subtractList);
            examTaskDetailUpdateWrapper.lambda()
                    .set(ExamTaskDetail::getExposedPaperType, StringUtils.isBlank(exposedPaper) ? null : exposedPaper)
                    .set(ExamTaskDetail::getUnexposedPaperType, StringUtils.isBlank(unexposedPaper) ? null : unexposedPaper)
                    .eq(ExamTaskDetail::getExamTaskId, examTaskDetail.getExamTaskId());
            this.update(examTaskDetailUpdateWrapper);
        }
    }

    /**
     * 卷库校验
     *
     * @param examTaskDetail
     */
    void validPaperUpdate(ExamTaskDetail examTaskDetail) {
        // 旧记录
        ExamTaskDetail examTaskDetailTemp = this.getByExamTaskId(examTaskDetail.getExamTaskId());
        if (examTaskDetailTemp == null) {
            throw ExceptionResultEnum.ERROR.exception("命题任务数据异常");
        }

        List<PaperInfoVo> oldPaperAttachmentIdsList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailTemp.getPaperAttachmentIds());
        List<PaperInfoVo> newPaperAttachmentIdsList = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
        // 卷型、题卡没有变动，直接return
        if (oldPaperAttachmentIdsList.size() == newPaperAttachmentIdsList.size()) {
            String oldString = oldPaperAttachmentIdsList.stream().sorted(Comparator.comparing(m -> m.getName())).map(m -> m.getName() + m.getAttachmentId()).collect(Collectors.joining());
            String newString = newPaperAttachmentIdsList.stream().sorted(Comparator.comparing(m -> m.getName())).map(m -> m.getName() + m.getAttachmentId()).collect(Collectors.joining());
            if (oldString.equals(newString) && examTaskDetailTemp.getPaperAttachmentIds().equals(examTaskDetail.getPaperAttachmentIds())) {
                return;
            }
        }

        ExamTask examTask = examTaskService.getById(examTaskDetail.getExamTaskId());
        // 是否审核中
        TFFlowApprove tfFlowApprove = tfFlowApproveService.findByFlowId(examTask.getFlowId());
        if (!tfFlowApprove.getStatus().equals(FlowStatusEnum.FINISH)) {
            throw ExceptionResultEnum.ERROR.exception("命题任务没有审核完成，不能修改卷库");
        }

        // 提交印刷的考场，不允许修改关联试卷
        List<ExamDetail> examDetails = examDetailService.listByCourseCodeAndPaperNumber(examTask.getSchoolId(), examTask.getExamId(), examTask.getCourseCode(), examTask.getPaperNumber());
        if (CollectionUtils.isNotEmpty(examDetails)) {
            List<ExamDetail> examDetails1 = examDetails.stream().filter(m -> !ExamDetailStatusEnum.NEW.equals(m.getStatus())
                    && !ExamDetailStatusEnum.READY.equals(m.getStatus())
                    && !ExamDetailStatusEnum.WAITING.equals(m.getStatus())).collect(Collectors.toList());
            if (!examDetails1.isEmpty()) {
                throw ExceptionResultEnum.ERROR.exception("印刷任务已开始打印，不能修改卷库");
            }
            Map<Long, List<Long>> map = examDetails.stream().collect(Collectors.groupingBy(ExamDetail::getPrintPlanId, Collectors.mapping(ExamDetail::getId, Collectors.toList())));
            for (Map.Entry<Long, List<Long>> listEntry : map.entrySet()) {
                int countRunning = tbTaskPdfService.countRunningByExamDetailIds(listEntry.getValue());
                if (countRunning > 0) {
                    throw ExceptionResultEnum.ERROR.exception("关联命题任务的考场正在生成打印文件，不能修改卷库");
                }
            }
        }

        // 已曝光试卷不能修改
        String exposedPaperType = examTaskDetailTemp.getExposedPaperType();
        if (StringUtils.isNotBlank(exposedPaperType)) {
            Map<String, String> oldPaperAttachmentIdMap = oldPaperAttachmentIdsList.stream().collect(Collectors.toMap(m -> m.getName(), m -> String.valueOf(m.getAttachmentId())));
            String[] exposedPaperTypes = exposedPaperType.split(",");
            String newPaperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
            if (StringUtils.isBlank(newPaperAttachmentIds)) {
                throw ExceptionResultEnum.ERROR.exception("已曝光试卷不能删除");
            } else {
                Map<String, String> newPaperAttachmentIdMap = newPaperAttachmentIdsList.stream().collect(Collectors.toMap(m -> m.getName(), m -> String.valueOf(m.getAttachmentId())));
                for (String paperType : exposedPaperTypes) {
                    if (!oldPaperAttachmentIdMap.containsKey(paperType)) {
                        throw ExceptionResultEnum.ERROR.exception("原试卷卷型[" + paperType + "]不存在");
                    }
                    if (!newPaperAttachmentIdMap.containsKey(paperType)) {
                        throw ExceptionResultEnum.ERROR.exception("已曝光卷型[" + paperType + "]不能删除");
                    }

                    String newAttachmentId = newPaperAttachmentIdMap.get(paperType);
                    String oldAttachmentId = oldPaperAttachmentIdMap.get(paperType);
                    if (!newAttachmentId.equals(oldAttachmentId)) {
                        throw ExceptionResultEnum.ERROR.exception("已曝光卷型[" + paperType + "]不能修改试卷文件");
                    }
                }
            }

            // 已有曝光卷型时，题卡不能修改
            if (!examTaskDetailTemp.getPaperAttachmentIds().equals(examTaskDetail.getPaperAttachmentIds())) {
                throw ExceptionResultEnum.ERROR.exception("已有试卷曝光，不能修改题卡");
            }
        }

        // 将旧题卡更新为未绑定
        List<PaperInfoVo> oldPaperAttachmentIdsJson = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetailTemp.getPaperAttachmentIds());
        List<Long> oldCardIds = oldPaperAttachmentIdsJson.stream().filter(m -> CardTypeEnum.CUSTOM.name().equals(m.getCardType())).map(m -> Long.parseLong(m.getCardId())).collect(Collectors.toList());
        for (Long cardId : oldCardIds) {
            UpdateWrapper<ExamCard> cardUpdateWrapper = new UpdateWrapper<>();
            cardUpdateWrapper.lambda().set(ExamCard::getUsed, false).eq(ExamCard::getId, cardId);
            examCardService.update(cardUpdateWrapper);
        }

        // 将新题卡更新为已绑定
        List<PaperInfoVo> newPaperAttachmentIdsJson = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds());
        List<Long> cardIds = newPaperAttachmentIdsJson.stream().filter(m -> CardTypeEnum.CUSTOM.name().equals(m.getCardType())).map(m -> Long.valueOf(m.getCardId())).collect(Collectors.toList());
        for (Long cardId : cardIds) {
            UpdateWrapper<ExamCard> cardUpdateWrapper = new UpdateWrapper<>();
            cardUpdateWrapper.lambda().set(ExamCard::getUsed, true).eq(ExamCard::getId, cardId);
            examCardService.update(cardUpdateWrapper);
        }
    }
}
