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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.boot.tools.excel.ExcelReader;
import com.qmth.boot.tools.excel.enums.ExcelType;
import com.qmth.distributed.print.business.bean.dto.FinalScoreDto;
import com.qmth.distributed.print.business.bean.dto.TCFinalScoreDto;
import com.qmth.distributed.print.business.bean.result.FinalScoreResult;
import com.qmth.distributed.print.business.bean.result.report.PaperStructDimensionResult;
import com.qmth.distributed.print.business.entity.TCFinalScore;
import com.qmth.distributed.print.business.entity.TCPaperStruct;
import com.qmth.distributed.print.business.enums.SourceEnum;
import com.qmth.distributed.print.business.mapper.TCFinalScoreMapper;
import com.qmth.distributed.print.business.service.PrintCommonService;
import com.qmth.distributed.print.business.service.TCFinalScoreService;
import com.qmth.distributed.print.business.service.TCPaperStructService;
import com.qmth.distributed.print.business.service.TRBasicInfoService;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicCourse;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.FieldUniqueEnum;
import com.qmth.teachcloud.common.service.BasicCourseService;
import com.qmth.teachcloud.common.util.JacksonUtil;
import com.qmth.teachcloud.common.util.RedisUtil;
import com.qmth.teachcloud.common.util.ResultUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import com.qmth.teachcloud.mark.bean.archivescore.MarkStudentScoreVo;
import com.qmth.teachcloud.mark.dto.mark.ScoreItem;
import com.qmth.teachcloud.mark.entity.MarkPaper;
import com.qmth.teachcloud.mark.service.MarkStudentService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 期末考试成绩表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2024-02-18
 */
@Service
public class TCFinalScoreServiceImpl extends ServiceImpl<TCFinalScoreMapper, TCFinalScore> implements TCFinalScoreService {
    private final static Logger log = LoggerFactory.getLogger(TCFinalScoreServiceImpl.class);

    @Resource
    PrintCommonService printCommonService;

    @Resource
    TCFinalScoreService tcFinalScoreService;

    @Resource
    MarkStudentService markStudentService;

    @Resource
    TRBasicInfoService trBasicInfoService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TCPaperStructService tcPaperStructService;

    @Resource
    BasicCourseService basicCourseService;

    /**
     * 导入期末成绩excel
     *
     * @param file
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Map<String, String> finalScoreExcelImport(MultipartFile file, Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) throws IOException {
        log.debug("导入Excel开始...");
        long start = System.currentTimeMillis();
        Map<String, String> messageMap = new LinkedHashMap<>();
        try {
            MarkPaper markPaper = printCommonService.scoreImportExcelVaild(file, examId, courseCode, paperNumber);
            Double totalScore = 100d;

            StringJoiner errorData = new StringJoiner("");
            StringJoiner successData = new StringJoiner("");

            ExcelReader excelReader = ExcelReader.create(ExcelType.XLSX, file.getInputStream(), 1);
            List<String[]> arrayList = excelReader.getDataArrayList();
            log.info("arrayList:{}", JacksonUtil.parseJson(arrayList));

            if (!CollectionUtils.isEmpty(arrayList)) {
                SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
                List<TCFinalScore> tcFinalScoreList = new ArrayList<>(arrayList.size());
                String[] columnNames = excelReader.getColumnNames();
                for (int i = 0; i < columnNames.length; i++) {
                    if (i == 0 && Objects.nonNull(columnNames[i]) && !Objects.equals(columnNames[i].trim(), "学号")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第一行为学号");
                    } else if (i == 1 && Objects.nonNull(columnNames[i]) && !Objects.equals(columnNames[i].trim(), "姓名")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第二行为姓名");
                    } else if (i == 2 && Objects.nonNull(columnNames[i]) && !Objects.equals(columnNames[i].trim(), "成绩")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第三行为成绩");
                    }
                }

                for (int i = 0; i < arrayList.size(); i++) {
                    String[] strs = arrayList.get(i);
                    if (Objects.nonNull(strs) && strs.length > 0) {
                        if (strs.length != columnNames.length) {
                            throw ExceptionResultEnum.ERROR.exception("excel列和数据不匹配");
                        }
                        boolean error = false;
                        List<TCFinalScoreDto> tcFinalScoreDtoList = new ArrayList<>();
                        TCFinalScore tcFinalScore = new TCFinalScore(examId, courseCode, markPaper.getCourseName(), paperNumber, obeCourseOutlineId, SourceEnum.EXCEL_IMPORT, sysUser.getId());
                        BigDecimal score = new BigDecimal(0);
                        for (int j = 0; j < strs.length; j++) {
                            if (Objects.isNull(strs[j]) || Objects.equals(strs[j].trim(), "")) {
                                errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[j] + "]为空;").add("\r\n");
                                error = true;
                            } else {
                                if (Objects.equals(columnNames[j].trim(), "学号")) {
                                    tcFinalScore.setStudentCode(strs[j]);
                                } else if (Objects.equals(columnNames[j].trim(), "姓名")) {
                                    tcFinalScore.setName(strs[j]);
                                } else if (Objects.equals(columnNames[j].trim(), "成绩")) {
                                    Double scoreTemp = SystemConstant.isDouble(strs[j]);
                                    if (Objects.nonNull(scoreTemp)) {
                                        tcFinalScore.setScore(scoreTemp);
                                    } else {
                                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[j]).add("]请输入数字").add("\r\n");
                                        error = true;
                                    }
                                } else {
                                    if (Objects.nonNull(SystemConstant.isDouble(strs[j]))) {
                                        score = score.add(new BigDecimal(strs[j]));
                                        tcFinalScoreDtoList.add(new TCFinalScoreDto(strs[j], columnNames[j]));
                                    } else {
                                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[j]).add("]请输入数字").add("\r\n");
                                        error = true;
                                    }
                                }
                            }
                        }
                        if (CollectionUtils.isNotEmpty(tcFinalScoreDtoList)) {
                            if (score.doubleValue() > totalScore.doubleValue()) {
                                errorData.add("excel第").add((i + 1) + "").add("行[总分大于试卷总分]").add("\r\n");
                                error = true;
                            } else {
                                score = score.setScale(2, BigDecimal.ROUND_HALF_UP);
                                tcFinalScore.setScore(Double.parseDouble(SystemConstant.df.format(score)));
                                tcFinalScore.setScoreDetail(JacksonUtil.parseJson(tcFinalScoreDtoList));
                            }
                        }
                        if (!error) {
                            tcFinalScoreList.add(tcFinalScore);
                        }
                    }
                }
                if (!CollectionUtils.isEmpty(tcFinalScoreList)) {
                    successData.add("共导入").add(tcFinalScoreList.size() + "").add("条数据");
                    List<TCFinalScore> tcFinalScoreDbSourceList = tcFinalScoreService.queryFinalScore(examId, courseCode, paperNumber, obeCourseOutlineId);
                    if (CollectionUtils.isEmpty(tcFinalScoreDbSourceList) || !CollectionUtils.isEqualCollection(tcFinalScoreDbSourceList, tcFinalScoreList)) {
                        tcFinalScoreService.removeByIds(tcFinalScoreDbSourceList.stream().map(s -> s.getId()).collect(Collectors.toList()));
                        tcFinalScoreService.saveBatch(tcFinalScoreList);
                        trBasicInfoService.clearReportData(examId, courseCode, paperNumber, obeCourseOutlineId, true);
                    }
                }
            }
            messageMap.put(SystemConstant.SUCCESS, successData.length() > 0 ? successData.toString() : "无");
            messageMap.put(SystemConstant.EXCEL_ERROR, errorData.length() > 0 ? errorData.toString() : "无");
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        log.debug("导入Excel结束...");
        long end = System.currentTimeMillis();
        log.info("============耗时{}秒============:", (end - start) / 1000);
        return messageMap;
    }

    /**
     * 期末成绩列表（分页）
     *
     * @param iPage
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     */
    @Override
    public IPage<TCFinalScore> finalScoreList(IPage<Map> iPage, Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        return this.baseMapper.finalScoreList(iPage, examId, courseCode, paperNumber, obeCourseOutlineId);
    }

    /**
     * 查找期末成绩分数图
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     */
    @Override
    public FinalScoreDto finalScoreScoreOverView(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        return this.baseMapper.finalScoreScoreOverView(examId, courseCode, paperNumber, obeCourseOutlineId);
    }

    /**
     * 查找期末成绩分数区间
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @param start
     * @param end
     * @return
     */
    @Override
    public int getCountByScoreRange(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId, Double start, Double end) {
        return this.baseMapper.getCountByScoreRange(examId, courseCode, paperNumber, obeCourseOutlineId, start, end);
    }

    /**
     * 查找考生成绩
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     */
    @Override
    public List<FinalScoreResult> examStudentOverview(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        return this.baseMapper.examStudentOverview(examId, courseCode, paperNumber, obeCourseOutlineId);
    }

    /**
     * 同步期末成绩
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Map<String, String> finalScoreSync(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) throws IOException {
        Map<String, String> messageMap = new LinkedHashMap<>();
        try {
            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
            StringJoiner errorData = new StringJoiner("");
            StringJoiner successData = new StringJoiner("");
            List<MarkStudentScoreVo> markStudentScoreVoList = markStudentService.listMarkStudentScoreList(examId, paperNumber);
            if (!CollectionUtils.isEmpty(markStudentScoreVoList)) {
                List<TCFinalScore> tcFinalScoreList = new ArrayList<>(markStudentScoreVoList.size());
                List<PaperStructDimensionResult> paperStructDimensionResultList = new ArrayList<>(markStudentScoreVoList.size());
                for (int i = 0; i < markStudentScoreVoList.size(); i++) {
                    MarkStudentScoreVo markStudentScoreVo = markStudentScoreVoList.get(i);
                    if (!CollectionUtils.isEmpty(markStudentScoreVo.getTotalScoreList())) {
                        List<TCFinalScoreDto> tcFinalScoreDtoList = new ArrayList<>();
                        for (ScoreItem scoreItem : markStudentScoreVo.getTotalScoreList()) {
                            String name = scoreItem.getMainNumber() + "-" + scoreItem.getSubNumber();
                            tcFinalScoreDtoList.add(new TCFinalScoreDto(scoreItem.getScore().toString(), name));
                            if (i == 0) {
                                paperStructDimensionResultList.add(new PaperStructDimensionResult(scoreItem.getMainNumber(), scoreItem.getSubNumber(), paperNumber, scoreItem.getTotalScore()));
                            }
                        }
                        tcFinalScoreList.add(new TCFinalScore(markStudentScoreVo, JacksonUtil.parseJson(tcFinalScoreDtoList), SourceEnum.SYNC, sysUser.getId(), obeCourseOutlineId));
                    }
                }
                if (!CollectionUtils.isEmpty(tcFinalScoreList) && !CollectionUtils.isEmpty(paperStructDimensionResultList)) {
                    successData.add("共同步").add(tcFinalScoreList.size() + "").add("条数据");
                    List<TCFinalScore> tcFinalScoreDbSourceList = tcFinalScoreService.queryFinalScore(examId, courseCode, paperNumber, obeCourseOutlineId);
                    if (CollectionUtils.isEmpty(tcFinalScoreDbSourceList) || !CollectionUtils.isEqualCollection(tcFinalScoreDbSourceList, tcFinalScoreList)) {
                        tcFinalScoreService.removeByIds(tcFinalScoreDbSourceList.stream().map(s -> s.getId()).collect(Collectors.toList()));
                        tcFinalScoreService.saveBatch(tcFinalScoreList);
                        trBasicInfoService.clearReportData(examId, courseCode, paperNumber, obeCourseOutlineId, true);
                    }
                    TCPaperStruct tcPaperStruct = tcPaperStructService.queryPaperStruct(examId, courseCode, null, obeCourseOutlineId);
                    if (Objects.nonNull(tcPaperStruct)) {
                        tcPaperStruct.setPaperNumber(paperNumber);
                        tcPaperStruct.setPaperStruct(JacksonUtil.parseJson(paperStructDimensionResultList));
                        tcPaperStruct.updateInfo(sysUser.getId());
                        tcPaperStructService.updateById(tcPaperStruct);
                    } else {
                        BasicCourse basicCourse = basicCourseService.getByCode(courseCode);
                        Objects.requireNonNull(basicCourse, "未找到基础课程信息");
                        tcPaperStruct = new TCPaperStruct(examId, courseCode, basicCourse.getName(), paperNumber, obeCourseOutlineId, JacksonUtil.parseJson(paperStructDimensionResultList), sysUser.getId());
                    }
                    tcPaperStructService.saveOrUpdate(tcPaperStruct);
                }
            }
            messageMap.put(SystemConstant.SUCCESS, successData.length() > 0 ? successData.toString() : "无");
            messageMap.put(SystemConstant.EXCEL_ERROR, errorData.length() > 0 ? errorData.toString() : "无");
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw ExceptionResultEnum.SQL_ERROR.exception("[" + FieldUniqueEnum.convertToTitle(columnStr) + "]数据不允许重复插入");
            } else if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, ((ApiException) e).getCode(), e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return messageMap;
    }

    /**
     * 根据考试id/课程编码/试卷编码删除数据
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     */
    @Override
    @Transactional
    public Boolean remove(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        QueryWrapper<TCFinalScore> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TCFinalScore::getExamId, examId).eq(TCFinalScore::getCourseCode, courseCode);
        if (Objects.nonNull(paperNumber)) {
            queryWrapper.lambda().and(w -> w.eq(TCFinalScore::getPaperNumber, paperNumber).or().isNull(TCFinalScore::getPaperNumber));
        }
        queryWrapper.lambda().eq(TCFinalScore::getTeachCourseId, obeCourseOutlineId);
        return tcFinalScoreService.remove(queryWrapper);
    }

    /**
     * 根据考试id/课程编码/试卷编码查询数据库数据
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     */
    @Override
    public List<TCFinalScore> queryFinalScore(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        QueryWrapper<TCFinalScore> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TCFinalScore::getExamId, examId).eq(TCFinalScore::getCourseCode, courseCode);
        if (Objects.nonNull(paperNumber)) {
            queryWrapper.lambda().and(w -> w.eq(TCFinalScore::getPaperNumber, paperNumber).or().isNull(TCFinalScore::getPaperNumber));
        }
        queryWrapper.lambda().eq(TCFinalScore::getTeachCourseId, obeCourseOutlineId);
        return tcFinalScoreService.list(queryWrapper);
    }

    /**
     * 获取期末考试同步锁
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     */
    @Override
    public void getFinalScoreSyncLock(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        String lockKey = SystemConstant.REDIS_FINAL_SCORE_DATA_FLOW_PREFIX + SystemConstant.SYNC + examId + "_" + courseCode + "_" + paperNumber + "_" + obeCourseOutlineId;
        Object o = redisUtil.get(lockKey);
        if (Objects.nonNull(o)) {
            throw ExceptionResultEnum.ERROR.exception("正在同步期末成绩数据，请稍候再试！");
        }
    }

    /**
     * 获取期末考试导入锁
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     */
    @Override
    public void getFinalScoreImportLock(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        String lockKey = SystemConstant.REDIS_FINAL_SCORE_DATA_FLOW_PREFIX + SystemConstant.IMPORT + examId + "_" + courseCode + "_" + paperNumber + "_" + obeCourseOutlineId;
        Object o = redisUtil.get(lockKey);
        if (Objects.nonNull(o)) {
            throw ExceptionResultEnum.ERROR.exception("正在导入期末成绩数据，请稍候再试！");
        }
    }
}
