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.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.distributed.print.business.bean.dto.ClazzContentDto;
import com.qmth.distributed.print.business.bean.dto.ExamTaskPlanStatisticDto;
import com.qmth.distributed.print.business.bean.dto.TCStatisticsDto;
import com.qmth.distributed.print.business.bean.result.TCStatisticResult;
import com.qmth.distributed.print.business.entity.ExamStudent;
import com.qmth.distributed.print.business.entity.TCStatistics;
import com.qmth.distributed.print.business.entity.TCStatisticsTemp;
import com.qmth.distributed.print.business.enums.BackupMethodEnum;
import com.qmth.distributed.print.business.enums.ClazzContentEnum;
import com.qmth.distributed.print.business.enums.ExamDetailStatusEnum;
import com.qmth.distributed.print.business.enums.StatisticsStatusEnum;
import com.qmth.distributed.print.business.mapper.TCStatisticsMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
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.ExceptionResultEnum;
import com.qmth.teachcloud.common.service.BasicStudentService;
import com.qmth.teachcloud.common.service.SysOrgService;
import com.qmth.teachcloud.common.util.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 命题统计表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2021-09-09
 */
@Service
public class TCStatisticsServiceImpl extends ServiceImpl<TCStatisticsMapper, TCStatistics> implements TCStatisticsService {

    @Resource
    TCStatisticsMapper tcStatisticsMapper;

    @Resource
    TCStatisticsTempService tcStatisticsTempService;

    @Resource
    SysOrgService sysOrgService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TeachStudentService teachStudentService;

    @Resource
    BasicStudentService basicStudentService;

    @Resource
    ExamDetailService examDetailService;

    @Resource
    ExamStudentService examStudentService;


    @Deprecated
    @Override
    public IPage<TCStatisticsDto> list(IPage<Map> iPage, Long semesterId, Long examId, Long collegeId, Long teachingRoomId, StatisticsStatusEnum status, String courseName, String teacherName, Long schoolId, Long userId) {
        return tcStatisticsMapper.list(iPage, semesterId, examId, collegeId, teachingRoomId, Objects.nonNull(status) ? status.name() : null, courseName, teacherName, schoolId, userId);
    }



    @Override
    @Transactional
    public void importJoinData(List<TCStatisticsTemp> tcStatisticsTempList) {
        List<TCStatistics> insertTCStatisticsList = new ArrayList<>();
        if (Objects.nonNull(tcStatisticsTempList) && tcStatisticsTempList.size() > 0) {
            Long requestUserId = tcStatisticsTempList.get(0).getCreateId();
            Long examId = tcStatisticsTempList.get(0).getExamId();

            for (TCStatisticsTemp tcStatisticsTemp : tcStatisticsTempList) {
                List<ClazzContentDto> clazzContentDtoList = new ArrayList<>();
                String courseCode = tcStatisticsTemp.getCourseCode();
                String clazzIds = tcStatisticsTemp.getClazzIds();
                StatisticsStatusEnum status = StatisticsStatusEnum.FINISH;
                // 导入的班级
                Set<Long> clazzIdSet = Arrays.stream(clazzIds.split(",")).map(SystemConstant::convertIdToLong).collect(Collectors.toSet());
                // 根据课程和考试查到的命题计划数据
                List<ExamTaskPlanStatisticDto> examTaskPlanStatisticDtoList = this.baseMapper.findByExamIdCourseCode(examId,courseCode);

                // 印刷计划备份数量map
                Map<Long,Integer> backupCountMap = new HashMap<>();
                int totalStudentCount = 0;
                boolean printSumView= true;
                for (Long clazzId : clazzIdSet) {
                    int studentCount = 0;
                    // 匹配到班级的所有命题计划
                    List<ExamTaskPlanStatisticDto> matchClazzList = examTaskPlanStatisticDtoList.stream().filter(e -> {
                        if (!SystemConstant.strNotNull(e.getClazzIds())){
                            return false;
                        }
                        Set<Long> ebcClazzIdSet = Arrays.stream(e.getClazzIds().split(",")).map(SystemConstant::convertIdToLong).collect(Collectors.toSet());
                        return ebcClazzIdSet.contains(clazzId);
                    }).collect(Collectors.toList());

                    ClazzContentDto clazzContent = new ClazzContentDto();
                    clazzContent.setClazzId(clazzId);
                    clazzContent.setClazzName(examDetailService.findClazzNamesByClazzIds(String.valueOf(clazzId), ","));

                    // 匹配到以印刷的
                    List<ExamTaskPlanStatisticDto> printClazzList = matchClazzList.stream().filter(e -> ExamDetailStatusEnum.FINISH.equals(e.getExamDetailStatus())).collect(Collectors.toList());

                    if (printClazzList.size() > 1) {
                        // 同一个班级课程已完成印刷的命题任务多次提交
                        printSumView = false;
                        status = StatisticsStatusEnum.UN_FINISH;
                        clazzContent.setStatus(ClazzContentEnum.EXCEPTION);
                    } else if (printClazzList.size() == 1) {
                        // 该班级对应的命题任务完成印刷
                        ExamTaskPlanStatisticDto printMatchClazz = printClazzList.get(0);
                        Long examDetailId = printMatchClazz.getExamDetailId();
                        clazzContent.setExamPrintPlanId(printMatchClazz.getExamPrintPlanId());
                        clazzContent.setExamPrintPlanName(printMatchClazz.getExamPrintPlanName());
                        clazzContent.setPaperNumber(printMatchClazz.getPaperNumber());
                        clazzContent.setStatus(ClazzContentEnum.FINISH);
                        studentCount = examStudentService.count(new QueryWrapper<ExamStudent>()
                                .lambda()
                                .eq(ExamStudent::getClazzId,clazzId)
                                .eq(ExamStudent::getExamDetailCourseId,printMatchClazz.getExamDetailCourseId()));
                        StringBuilder printCount = new StringBuilder(String.valueOf(studentCount));
                        // 如果是按照考场备份再找备份数量
                        if (BackupMethodEnum.ROOM.equals(printMatchClazz.getBackupMethod())){
                            // 如果是新的考场再加
                            if (!backupCountMap.containsKey(examDetailId)){
                                int backupCount = printMatchClazz.getBackupCount();
                                backupCountMap.put(examDetailId,backupCount);
                                if (backupCount > 0){
                                    printCount.append(" + ").append(backupCount);
                                }
                            }
                        }
                        clazzContent.setPrintCount(printCount.toString());
                    } else {
                        // 该班级对应的命题任务还未完成印刷
                        if (matchClazzList.size() == 1) {
                            // 课程-班级只提交一次（且已有印刷计划）的数据记录印刷计划试卷编号等信息
                            ExamTaskPlanStatisticDto matchClazz = matchClazzList.get(0);
                            Long examDetailId = matchClazz.getExamDetailId();
                            clazzContent.setExamPrintPlanId(matchClazz.getExamPrintPlanId());
                            clazzContent.setExamPrintPlanName(matchClazz.getExamPrintPlanName());
                            clazzContent.setPaperNumber(matchClazz.getPaperNumber());
                            studentCount = examStudentService.count(new QueryWrapper<ExamStudent>()
                                    .lambda()
                                    .eq(ExamStudent::getClazzId, clazzId)
                                    .eq(ExamStudent::getExamDetailCourseId, matchClazz.getExamDetailCourseId()));
                            StringBuilder printCount = new StringBuilder(String.valueOf(studentCount));
                            // 如果是按照考场备份再找备份数量
                            if (BackupMethodEnum.ROOM.equals(matchClazz.getBackupMethod())){
                                // 如果是新的考场再加
                                if (!backupCountMap.containsKey(examDetailId)){
                                    int backupCount = matchClazz.getBackupCount();
                                    backupCountMap.put(examDetailId,backupCount);
                                    printCount.append(" + ").append(backupCount);
                                }
                            }
                            clazzContent.setPrintCount(printCount.toString());
                        }
                        status = StatisticsStatusEnum.UN_FINISH;
                        clazzContent.setStatus(ClazzContentEnum.UN_FINISH);
                    }
                    clazzContentDtoList.add(clazzContent);
                    totalStudentCount = totalStudentCount + studentCount;
                }

                TCStatistics tcStatistics = new TCStatistics();
                tcStatistics.setExamId(examId);
                tcStatistics.setBatchNo(tcStatisticsTemp.getBatchNo());
                tcStatistics.setCollegeId(tcStatisticsTemp.getCollegeId());
                tcStatistics.setCollegeName(tcStatisticsTemp.getCollegeName());
                tcStatistics.setTeachingRoomId(tcStatisticsTemp.getTeachingRoomId());
                tcStatistics.setTeachingRoomName(tcStatisticsTemp.getTeachingRoomName());
                tcStatistics.setCourseName(tcStatisticsTemp.getCourseName());
                tcStatistics.setCourseCode(tcStatisticsTemp.getCourseCode());
                tcStatistics.setTeacherName(tcStatisticsTemp.getTeacherName());
                tcStatistics.setClazzIds(clazzIds);
                tcStatistics.setClazzName(examDetailService.findClazzNamesByClazzIds(clazzIds,","));
                tcStatistics.setClazzContent(JSON.toJSONString(clazzContentDtoList));
                tcStatistics.setStatus(status);
                tcStatistics.insertInfo(requestUserId);
                if (StatisticsStatusEnum.FINISH.equals(status)){
                    ClazzContentDto cell = clazzContentDtoList.get(0);
                    tcStatistics.setPrintPlanId(cell.getExamPrintPlanId());
                    tcStatistics.setPaperNumber(cell.getPaperNumber());
                }
                // 考场id
                int backupTotal = 0;
                for (Long examDetailId : backupCountMap.keySet()) {
                    int backupCount = backupCountMap.get(examDetailId);
                    backupTotal = backupTotal + backupCount;
                }
                String printSum = String.valueOf(totalStudentCount);
                if (backupTotal > 0){
                    printSum = printSum + " + " + backupTotal;
                }
                if (printSumView){
                    tcStatistics.setPrintSum(printSum);
                }
                insertTCStatisticsList.add(tcStatistics);
            }
        }
        this.saveBatch(insertTCStatisticsList);
    }

    @Override
    @Transactional
    public void removeImportData(Long examId, Long userId) {
        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
        QueryWrapper<TCStatistics> tcStatisticsQueryWrapper = new QueryWrapper<>();
        tcStatisticsQueryWrapper.lambda()
                .eq(TCStatistics::getExamId, examId)
                .eq(TCStatistics::getCreateId, userId);
        tcStatisticsService.remove(tcStatisticsQueryWrapper);

        QueryWrapper<TCStatisticsTemp> tcStatisticsTempQueryWrapper = new QueryWrapper<>();
        tcStatisticsTempQueryWrapper.lambda()
                .eq(TCStatisticsTemp::getExamId, examId)
                .eq(TCStatisticsTemp::getCreateId, userId);
        tcStatisticsTempService.remove(tcStatisticsTempQueryWrapper);
    }

    @Override
    @Transactional
    public Result freshenData() {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        QueryWrapper<TCStatistics> tcStatisticsQueryWrapper = new QueryWrapper<>();
        tcStatisticsQueryWrapper.select(" DISTINCT batch_no ").eq("create_id", sysUser.getId());
        List<TCStatistics> tcStatisticsList = this.list(tcStatisticsQueryWrapper);
        if (Objects.nonNull(tcStatisticsList) && tcStatisticsList.size() > 0) {
            Set<String> batchNoSet = tcStatisticsList.stream().map(s -> s.getBatchNo()).collect(Collectors.toSet());
            boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_BATCH_NO_PREFIX + Math.abs(batchNoSet.toString().hashCode()), SystemConstant.REDIS_LOCK_BATCH_NO_TIME_OUT);
            if (!lock) {
                throw ExceptionResultEnum.ERROR.exception("正在刷新数据，请稍候再试！");
            }
            try {
                this.freshenJoinData(sysUser, batchNoSet);
            } catch (Exception e) {
                log.error(SystemConstant.LOG_ERROR, e);
                if (e instanceof ApiException) {
                    return ResultUtil.error((ApiException) e, e.getMessage());
                } else {
                    return ResultUtil.error(e.getMessage());
                }
            } finally {
                redisUtil.releaseLock(SystemConstant.REDIS_LOCK_BATCH_NO_PREFIX + Math.abs(batchNoSet.toString().hashCode()));
            }
        }
        return ResultUtil.ok(true);
    }

    @Override
    @Transactional
    public Result deleteData(List<Long> ids) {
        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
        tcStatisticsService.removeByIds(ids);
        return ResultUtil.ok(true);
    }

    @Override
    public IPage<TCStatisticResult> findExamTaskPrintStatistic(Long semesterId, Long examId, Long collegeId, Long teachingRoomId, StatisticsStatusEnum status, String courseName, String teacherName, Long schoolId, Long userId, Integer pageNumber, Integer pageSize) {
        return tcStatisticsMapper.findExamTaskPrintStatistic(new Page<>(pageNumber,pageSize),semesterId, examId, collegeId, teachingRoomId, status, courseName, teacherName, userId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void refreshTCStatisticByExamIdAndRequestUser(Long examId, SysUser requestUser) {
        Long requestUserId =  requestUser.getId();
        List<TCStatistics> tcStatisticsList = this.list(new QueryWrapper<TCStatistics>().lambda().eq(TCStatistics::getExamId,examId).eq(TCStatistics::getCreateId,requestUserId));
        if (tcStatisticsList != null && tcStatisticsList.size() > 0){
            String key = SystemConstant.REDIS_LOCK_BATCH_NO_PREFIX + Math.abs((examId + String.valueOf(requestUserId)).hashCode());
            boolean lock = redisUtil.lock(key, SystemConstant.REDIS_LOCK_BATCH_NO_TIME_OUT);
            if (!lock){
                throw ExceptionResultEnum.ERROR.exception("正在刷新数据，请稍候再试！");
            }
            try {
                // 删除该用户现在的本次考试的统计数据
                this.removeImportData(examId,requestUserId);
                List<TCStatisticsTemp> tcStatisticsTempList = tcStatisticsList.stream().flatMap(e ->{
                    TCStatisticsTemp tcStatisticsTemp = new TCStatisticsTemp(
                            examId,
                            e.getCollegeId(),
                            e.getCollegeName(),
                            e.getTeachingRoomId(),
                            e.getTeachingRoomName(),
                            e.getCourseName(),
                            e.getCourseCode(),
                            e.getTeacherName(),
                            e.getClazzIds(),
                            e.getClazzName(),
                            e.getBatchNo(),
                            requestUserId);
                    return Stream.of(tcStatisticsTemp);
                }).collect(Collectors.toList());
                this.importJoinData(tcStatisticsTempList);
            }catch (Exception e){
                log.error(SystemConstant.LOG_ERROR, e);
            } finally {
                redisUtil.releaseLock(key);
            }
        }
    }

    /**
     * 关联数据获取orgIds
     *
     * @param sysUser
     * @param batchNo
     * @param tcStatisticsTempList
     * @return
     */
    private Set<Long> joinDataGetOrgIds(SysUser sysUser, String batchNo, List<TCStatisticsTemp> tcStatisticsTempList) {
        Set<Long> collegeIdSet = new HashSet<>();
        for (TCStatisticsTemp t : tcStatisticsTempList) {
            collegeIdSet.add(t.getCollegeId());
            t.insertInfo(sysUser.getId());
            t.setBatchNo(batchNo);
        }
        tcStatisticsTempService.saveBatch(tcStatisticsTempList);

        Set<Long> orgIds = new HashSet<>();
        for (Long l : collegeIdSet) {
            List<SysOrg> sysOrgList = sysOrgService.findByConnectByRootOrgId(l);
            Set<Long> orgTempIds = sysOrgList.stream().map(s -> s.getId()).collect(Collectors.toSet());
            orgIds.addAll(orgTempIds);
        }
        return orgIds;
    }

    /**
     * 保存正式统计数据
     *
     * @param sysUser
     * @param tcStatisticsDtoList
     */
    private void saveJoinData(SysUser sysUser, List<TCStatisticsDto> tcStatisticsDtoList) {
        //进行过滤
        Map<String, TCStatisticsDto> map = new LinkedHashMap<>();
        for (TCStatisticsDto t : tcStatisticsDtoList) {
            String key = t.getCollegeId() + ":" +
                    t.getTeachingRoomId() + ":" +
                    t.getCourseCode() + ":" +
                    t.getPaperNumber() + ":" +
                    t.getPrintPlanId() + ":" +
                    t.getClazzIds();
            if (!map.containsKey(key)) {
                map.computeIfAbsent(key, v -> t);
            } else {
                TCStatisticsDto temp = map.get(key);
                if (Integer.parseInt(t.getPrintSum()) > Integer.parseInt(temp.getPrintSum())) {
                    map.put(key, t);
                } else if (Integer.parseInt(t.getPrintSum()) == Integer.parseInt(temp.getPrintSum())) {
                    if (t.getStatus() != StatisticsStatusEnum.UN_JOIN) {
                        temp.setExamDetailId(t.getExamDetailId());
                    } else {
                        temp.setExamDetailId(null);
                    }
                }
            }
        }
        List<TCStatistics> tcStatisticsList = new Gson().fromJson(JacksonUtil.parseJson(map.values()), new TypeToken<List<TCStatistics>>() {
        }.getType());
        for (TCStatistics t : tcStatisticsList) {
            t.insertInfo(sysUser.getId());
        }
        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
        tcStatisticsService.saveBatch(tcStatisticsList);
    }

    /**
     * 刷新关联数据
     *
     * @param sysUser
     * @param batchNoSet
     */
    public void freshenJoinData(SysUser sysUser, Set<String> batchNoSet) {
        TCStatisticsService tcStatisticsService = SpringContextHolder.getBean(TCStatisticsService.class);
        for (String batchNo : batchNoSet) {
            List<TCStatisticsTemp> tcStatisticsTempList = tcStatisticsTempService.findByBatchNoJoin(sysUser.getSchoolId(), batchNo);
            if (Objects.nonNull(tcStatisticsTempList) && tcStatisticsTempList.size() > 0) {
                QueryWrapper<TCStatisticsTemp> tcStatisticsTempQueryWrapper = new QueryWrapper<>();
                tcStatisticsTempQueryWrapper.lambda().eq(TCStatisticsTemp::getBatchNo, batchNo);
                tcStatisticsTempService.remove(tcStatisticsTempQueryWrapper);

                String batchNoNew = SystemConstant.getUuid();
                Set<Long> orgIds = this.joinDataGetOrgIds(sysUser, batchNoNew, tcStatisticsTempList);
                List<TCStatisticsDto> tcStatisticsDtoList = this.findByBatchNoCountJoin(sysUser.getSchoolId(), batchNoNew, orgIds);
                if (Objects.nonNull(tcStatisticsDtoList) && tcStatisticsDtoList.size() > 0) {
                    QueryWrapper<TCStatistics> tcStatisticsQueryWrapper = new QueryWrapper<>();
                    tcStatisticsQueryWrapper.lambda().eq(TCStatistics::getBatchNo, batchNo);
                    tcStatisticsService.remove(tcStatisticsQueryWrapper);
                    this.saveJoinData(sysUser, tcStatisticsDtoList);
                }
            }
        }
    }

    /**
     * 根据batchNo统计信息
     *
     * @param schoolId
     * @param batchNo
     * @param orgIds
     * @return
     */
    public List<TCStatisticsDto> findByBatchNoCount(Long schoolId, String batchNo, Set<Long> orgIds) {
        return tcStatisticsMapper.findByBatchNoCount(schoolId, batchNo, orgIds);
    }

    /**
     * 根据batchNo统计信息
     *
     * @param schoolId
     * @param batchNo
     * @param orgIds
     * @return
     */
    public List<TCStatisticsDto> findByBatchNoCountJoin(Long schoolId, String batchNo, Set<Long> orgIds) {
        return tcStatisticsMapper.findByBatchNoCountJoin(schoolId, batchNo, orgIds);
    }
}
