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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.reflect.TypeToken;
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.excel.PaperStructDto;
import com.qmth.distributed.print.business.bean.result.ObeCourseWeightResult;
import com.qmth.distributed.print.business.bean.result.report.PaperStructDimensionResult;
import com.qmth.distributed.print.business.entity.TCPaperStruct;
import com.qmth.distributed.print.business.mapper.TCPaperStructMapper;
import com.qmth.distributed.print.business.service.*;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.MarkQuestion;
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.util.*;
import com.qmth.teachcloud.mark.entity.MarkPaper;
import com.qmth.teachcloud.mark.service.MarkQuestionService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
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.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 试卷蓝图表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2024-02-18
 */
@Service
public class TCPaperStructServiceImpl extends ServiceImpl<TCPaperStructMapper, TCPaperStruct> implements TCPaperStructService {
    private final static Logger log = LoggerFactory.getLogger(TCPaperStructServiceImpl.class);

    @Resource
    TCPaperStructService tcPaperStructService;

    @Resource
    TCFinalScoreService tcFinalScoreService;

    @Resource
    TRBasicInfoService trBasicInfoService;

    @Resource
    PrintCommonService printCommonService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    ObeCourseTargetService obeCourseTargetService;

    @Resource
    MarkQuestionService markQuestionService;

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

    /**
     * 导入期末成绩-试卷结构excel
     *
     * @param file
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Map<String, String> paperStructExcelImport(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);
            StringJoiner errorData = new StringJoiner("");
            StringJoiner successData = new StringJoiner("");

            ExcelReader excelReader = ExcelReader.create(ExcelType.XLSX, file.getInputStream(), 1);
            List<PaperStructDto> paperStructDtoList = excelReader.getObjectList(PaperStructDto.class);
            log.info("paperStructDtoList:{}", JacksonUtil.parseJson(paperStructDtoList));

            if (!CollectionUtils.isEmpty(paperStructDtoList)) {
                SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
                String[] columnNames = excelReader.getColumnNames();
                for (int i = 0; i < columnNames.length; i++) {
                    if (i == 0 && Objects.nonNull(columnNames[i]) && !columnNames[i].trim().contains("大题名称")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第一行为大题名称");
                    } else if (i == 1 && Objects.nonNull(columnNames[i]) && !columnNames[i].trim().contains("大题号")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第二行为大题号");
                    } else if (i == 2 && Objects.nonNull(columnNames[i]) && !columnNames[i].trim().contains("小题号")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第三行为小题号");
                    } else if (i == 3 && Objects.nonNull(columnNames[i]) && !columnNames[i].trim().contains("小题满分")) {
                        throw ExceptionResultEnum.ERROR.exception("excel表头第四行为小题满分");
                    }
                }

                Double totalScore = 0.0d;
                List<PaperStructDto> paperStructDtoNewList = new ArrayList<>(paperStructDtoList.size());
                for (int i = 0; i < paperStructDtoList.size(); i++) {
                    PaperStructDto paperStructDto = paperStructDtoList.get(i);
                    boolean error = false;
                    if (Objects.isNull(paperStructDto.getMainTitle()) || Objects.equals(paperStructDto.getMainTitle().trim(), "")) {
                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[0] + "]为空;").add("\r\n");
                        error = true;
                    }
                    if (Objects.isNull(paperStructDto.getMainNumber()) || Objects.equals(paperStructDto.getMainNumber(), "")) {
                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[1] + "]为空;").add("\r\n");
                        error = true;
                    }
                    if (Objects.isNull(paperStructDto.getSubNumber()) || Objects.equals(paperStructDto.getSubNumber(), "")) {
                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[2] + "]为空;").add("\r\n");
                        error = true;
                    }
                    if (Objects.isNull(paperStructDto.getScore()) || Objects.equals(paperStructDto.getScore(), "")) {
                        errorData.add("excel第").add((i + 1) + "").add("行[").add(columnNames[3] + "]为空;").add("\r\n");
                        error = true;
                    }
                    if (!error) {
                        totalScore = totalScore + paperStructDto.getScore();
                        paperStructDtoNewList.add(paperStructDto);
                    }
                }
                if (totalScore.doubleValue() < 100d) {
                    throw ExceptionResultEnum.ERROR.exception("试卷满分小于100分");
                }
                if (!CollectionUtils.isEmpty(paperStructDtoNewList)) {
                    successData.add("共导入").add(paperStructDtoNewList.size() + "").add("条数据");
                    TCPaperStruct tcPaperStruct = new TCPaperStruct(examId, courseCode, markPaper.getCourseName(), paperNumber, obeCourseOutlineId, JacksonUtil.parseJson(paperStructDtoNewList), totalScore, 60d, sysUser.getId());
                    TCPaperStruct tcPaperStructDb = tcPaperStructService.queryPaperStruct(examId, courseCode, paperNumber, obeCourseOutlineId);
                    if (Objects.nonNull(tcPaperStructDb) && !tcPaperStructDb.equals(tcPaperStruct)) {
                        tcFinalScoreService.remove(examId, courseCode, paperNumber, obeCourseOutlineId);
                        trBasicInfoService.clearReportData(examId, courseCode, paperNumber, obeCourseOutlineId, true);
                    }
                    tcPaperStructService.remove(examId, courseCode, paperNumber, obeCourseOutlineId);
                    tcPaperStructService.save(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());
            }
        }
        log.debug("导入Excel结束...");
        long end = System.currentTimeMillis();
        log.info("============耗时{}秒============:", (end - start) / 1000);
        return messageMap;
    }

    /**
     * 同步试卷结构
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Map<String, String> paperStructSync(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<PaperStructDimensionResult> paperStructDimensionResultList = obeCourseTargetService.findTikuPaperStruct(obeCourseOutlineId, paperNumber, examId);
            if (CollectionUtils.isEmpty(paperStructDimensionResultList)) {
                throw ExceptionResultEnum.ERROR.exception("未查询到题和知识点对应关系请手动关联");
            }
            List<String> paperStructList = new ArrayList<>(paperStructDimensionResultList.size()), paperStructDbList = new ArrayList<>(paperStructDimensionResultList.size());
            for (PaperStructDimensionResult p : paperStructDimensionResultList) {
                Objects.requireNonNull(p.getMainNumber(), "大题号为空");
                Objects.requireNonNull(p.getSubNumber(), "小题号为空");
                if (!CollectionUtils.isEmpty(p.getTargetList()) && p.getTargetList().size() > 1) {
                    throw ExceptionResultEnum.ERROR.exception("一个题只能属于一个目标");
                }
                paperStructList.add(p.getMainNumber() + "_" + p.getSubNumber() + "_" + p.getScore());
            }
            successData.add("共同步").add(paperStructDimensionResultList.size() + "").add("条数据");
            TCPaperStruct tcPaperStructDb = tcPaperStructService.queryPaperStruct(examId, courseCode, paperNumber, obeCourseOutlineId);
            TCPaperStruct tcPaperStruct = null;
            if (Objects.nonNull(tcPaperStructDb) && Objects.nonNull(tcPaperStructDb.getPaperStructDimension())) {
                tcPaperStruct = new TCPaperStruct();
                BeanUtils.copyProperties(tcPaperStructDb, tcPaperStruct);
                tcPaperStruct.setPaperStructDimension(JacksonUtil.parseJson(paperStructDimensionResultList));

                if (Objects.nonNull(tcPaperStructDb.getPaperStruct())) {
                    List<PaperStructDto> paperStructDtoList = GsonUtil.fromJson(tcPaperStruct.getPaperStruct(), new TypeToken<List<PaperStructDto>>() {
                    }.getType());
                    paperStructDbList = paperStructDtoList.stream().map(e -> e.getMainNumber() + "_" + e.getSubNumber() + "_" + e.getScore()).collect(Collectors.toList());
                } else {
                    List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndPaperType(examId, paperNumber, null);
                    if (CollectionUtils.isEmpty(markQuestionList)) {
                        throw ExceptionResultEnum.ERROR.exception("未找到试卷结构");
                    }
                    paperStructDbList = markQuestionList.stream().map(e -> e.getMainNumber() + "_" + e.getSubNumber() + "_" + e.getTotalScore()).collect(Collectors.toList());
                }
                if (!CollectionUtils.isEmpty(paperStructDbList) && !CollectionUtils.isEmpty(paperStructList) && !CollectionUtils.isEqualCollection(paperStructDbList, paperStructList)) {
                    Collections.sort(paperStructDbList);
                    Collections.sort(paperStructList);

                    tcFinalScoreService.remove(examId, courseCode, paperNumber, obeCourseOutlineId);
                    trBasicInfoService.clearReportData(examId, courseCode, paperNumber, obeCourseOutlineId, false);
                    tcPaperStruct.setPaperStruct(null);
                }
                tcPaperStruct.updateInfo(sysUser.getId());
            } else {
                tcPaperStructService.remove(examId, courseCode, paperNumber, obeCourseOutlineId);
                MarkPaper markPaper = printCommonService.getMarkPaper(examId, courseCode, paperNumber);
                ObeCourseWeightResult obeCourseWeightResult = trBasicInfoService.findCourseWeightResultRmi(obeCourseOutlineId);
                tcPaperStruct = new TCPaperStruct(examId, courseCode, markPaper.getCourseName(), paperNumber, obeCourseOutlineId, JacksonUtil.parseJson(paperStructDimensionResultList), sysUser.getId(), obeCourseWeightResult.getDimensionSign());
                List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndPaperType(examId, paperNumber, null);
                if (CollectionUtils.isEmpty(markQuestionList)) {
                    throw ExceptionResultEnum.ERROR.exception("未找到试卷结构");
                }
                paperStructDbList = markQuestionList.stream().map(e -> e.getMainNumber() + "_" + e.getSubNumber() + "_" + e.getTotalScore()).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(paperStructDbList) && !CollectionUtils.isEmpty(paperStructList) && !CollectionUtils.isEqualCollection(paperStructDbList, paperStructList)) {
                    Collections.sort(paperStructDbList);
                    Collections.sort(paperStructList);

                    tcFinalScoreService.remove(examId, courseCode, paperNumber, obeCourseOutlineId);
                    trBasicInfoService.clearReportData(examId, courseCode, paperNumber, obeCourseOutlineId, false);
                }
            }
            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
    public TCPaperStruct queryPaperStruct(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        QueryWrapper<TCPaperStruct> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TCPaperStruct::getExamId, examId).eq(TCPaperStruct::getCourseCode, courseCode);
        if (Objects.nonNull(paperNumber)) {
            queryWrapper.lambda().and(w -> w.eq(TCPaperStruct::getPaperNumber, paperNumber).or().isNull(TCPaperStruct::getPaperNumber));
        }
        queryWrapper.lambda().eq(TCPaperStruct::getTeachCourseId, obeCourseOutlineId);
        return tcPaperStructService.getOne(queryWrapper);
    }

    /**
     * 获取试卷结构同步锁
     *
     * @param examId
     * @param courseCode
     * @param paperNumber
     * @param obeCourseOutlineId
     */
    @Override
    public void getPaperStructSyncLock(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        String lockKey = SystemConstant.REDIS_PAPER_STRUCT_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
     * @param obeCourseOutlineId
     */
    @Override
    public void getPaperStructImportLock(Long examId, String courseCode, String paperNumber, Long obeCourseOutlineId) {
        String lockKey = SystemConstant.REDIS_PAPER_STRUCT_FLOW_PREFIX + SystemConstant.IMPORT + examId + "_" + courseCode + "_" + paperNumber + "_" + obeCourseOutlineId;
        Object o = redisUtil.get(lockKey);
        if (Objects.nonNull(o)) {
            throw ExceptionResultEnum.ERROR.exception("正在导入试卷蓝图数据，请稍候再试！");
        }
    }
}
