<template>
  <div class="question-export-edit">
    <el-dialog
      custom-class="question-export-edit-dialog"
      :visible.sync="modalIsShow"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      append-to-body
      fullscreen
      destroy-on-close
      :show-close="false"
      @opened="visibleChange"
      @closed="initData"
    >
      <div slot="title" class="box-justify">
        <div>
          <h2>文件上传</h2>
        </div>
        <div>
          <el-button
            size="small"
            type="danger"
            plain
            icon="icon icon-back"
            @click="cancel"
            >返回</el-button
          >
        </div>
      </div>
      <div class="qe-body">
        <div class="qe-part qe-part-edit">
          <div class="qe-part-main">
            <div class="qe-part-head">
              <h3>题目编辑</h3>
              <div>
                <i class="icon icon-tips"></i>
                提示：若识别有误，可点击左侧题目按格式进行修改后重新识别
              </div>
            </div>
            <div class="qe-part-body">
              <v-editor
                v-model="paperRichJson"
                :enable-formula="false"
                :enable-audio="false"
              ></v-editor>
            </div>
          </div>
        </div>
        <div class="qe-part qe-part-view">
          <div class="qe-part-main">
            <div class="qe-part-head">
              <h3>题目阅览</h3>
              <div>
                <el-button
                  size="small"
                  type="primary"
                  plain
                  icon="icon icon-export-answer"
                  @click="toImportAnswer"
                  >导入答案属性</el-button
                >
                <el-button
                  size="small"
                  type="primary"
                  icon="icon icon-save-white"
                  @click="confirm"
                  >识别无误，加入题库</el-button
                >
              </div>
            </div>
            <div class="qe-part-body">
              <question-import-paper-edit
                v-if="paperData.length"
                ref="QuestionImportPaperEdit"
                :key="questionKey"
                :paper="paperData"
                :course-id="data.importData.courseId"
              ></question-import-paper-edit>
            </div>
          </div>
        </div>
        <div class="qe-middle">
          <div class="qe-middle-arrow"></div>
          <el-button
            size="small"
            type="primary"
            :loading="loading"
            @click="toParse"
            >识别</el-button
          >
        </div>
      </div>
    </el-dialog>

    <!-- 上传答案文件 -->
    <import-file-dialog
      ref="ImportAnswerDialog"
      dialog-title="导入答案"
      :template-download-handle="answerTemplateDownload"
      :upload-url="uploadAnswerUrl"
      :upload-data="uploadAnswerData"
      add-file-param="dataFile"
      @uploaded="answerUploaded"
    ></import-file-dialog>
  </div>
</template>

<script>
// import paperRichTextJson from "../datas/paperRichText.json";
// import paperParseData from "../datas/paperParseData.json";

import { calcSum, deepCopy, objTypeOf, randomCode } from "@/plugins/utils";
import QuestionImportPaperEdit from "./QuestionImportPaperEdit.vue";
import { isAnEmptyRichText } from "@/utils/utils";
import {
  questionImportPaperSave,
  questionImportParseRichText,
  questionImportDownloadTemplate,
} from "../api";
import ImportFileDialog from "@/components/ImportFileDialog.vue";
import { QUESTION_API } from "@/constants/constants";
import { propertyNameQueryApi } from "@/modules/question/api";
import { downloadByApi } from "@/plugins/download";

const questionInfoField = [
  "courseId",
  "difficulty",
  "quesProperties",
  "score",
  "publicity",
  "control",
  "answerAnalysis",
  "quesAnswer",
];

export default {
  name: "QuestionExportEdit",
  components: { QuestionImportPaperEdit, ImportFileDialog },
  props: {
    data: {
      type: Object,
      default() {
        return {
          richText: { sections: [] },
          detailInfo: [],
          importData: {
            courseId: "",
            courseName: "",
            name: "",
            checkTotalScore: false,
            useOriginalPaper: false,
            totalScore: 0,
          },
        };
      },
    },
  },
  data() {
    return {
      modalIsShow: false,
      loading: false,
      questionKey: "",
      paperData: [],
      paperRichJson: { sections: [] },
      // upload answer
      uploadAnswerUrl: `${QUESTION_API}/word/parse/import`,
      uploadAnswerData: {},
    };
  },
  methods: {
    async visibleChange() {
      await this.getCourseProperty();
      // this.paperData = deepCopy(paperParseData);
      // this.paperRichJson = deepCopy(paperRichTextJson);
      this.paperRichJson = deepCopy(this.data.richText);
      this.paperData = deepCopy(this.data.detailInfo);
      this.questionKey = randomCode();
    },
    async getCourseProperty() {
      const res = await propertyNameQueryApi(this.data.importData.courseId, "");
      const optionList = res.data || [];
      window.sessionStorage.setItem(
        "coursePropertys",
        JSON.stringify({ optionList, courseId: this.data.importData.courseId })
      );
    },
    initData() {
      this.paperData = [];
      this.paperRichJson = { sections: [] };
      window.sessionStorage.removeItem("coursePropertys");
      this.$message.closeAll();
    },
    cancel() {
      this.modalIsShow = false;
    },
    open() {
      this.modalIsShow = true;
    },
    async toParse() {
      if (isAnEmptyRichText(this.paperRichJson)) {
        this.$message.error("请输入试卷内容！");
        return;
      }

      if (this.loading) return;
      this.loading = true;

      const res = await questionImportParseRichText({
        richText: this.paperRichJson,
        courseId: this.data.importData.courseId,
      }).catch(() => {});
      this.loading = false;
      if (!res) return;

      const cacheData = this.getCachePaperInfo(
        this.getImportPaperData(),
        questionInfoField
      );
      // console.log(cacheData);
      this.paperData = this.assignCachePaperData(res.data, cacheData);
      this.questionKey = randomCode();
    },
    getCachePaperInfo(paperData, cacheFields = []) {
      let cachePaperInfo = {};
      paperData.forEach((detail, dIndex) => {
        detail.questionInfo.forEach((question, qIndex) => {
          let info = {};
          let k = `${dIndex + 1}_${qIndex + 1}`;
          if (cacheFields.length) {
            cacheFields.forEach((field) => {
              info[field] = question[field];
            });
          } else {
            info = { ...question };
          }

          cachePaperInfo[k] = info;

          if (question.subQuestions && question.subQuestions.length) {
            question.subQuestions.forEach((subq, subqIndex) => {
              let info = {};
              let k = `${dIndex + 1}_${qIndex + 1}_${subqIndex + 1}`;
              if (cacheFields.length) {
                cacheFields.forEach((field) => {
                  info[field] = subq[field];
                });
              } else {
                info = { ...subq };
              }

              cachePaperInfo[k] = info;
            });
          }
        });
      });
      // console.log(cachePaperInfo);
      return cachePaperInfo;
    },
    assignCachePaperData(paperData, cacheData, mergeReverse = false) {
      return paperData.map((detail, dIndex) => {
        detail.questions = detail.questions.map((question, qIndex) => {
          let k = `${dIndex + 1}_${qIndex + 1}`;
          let nq = this.mergeObjData(
            question,
            cacheData[k] || {},
            mergeReverse
          );
          if (question.subQuestions && question.subQuestions.length) {
            nq.subQuestions = question.subQuestions.map((subq, subqIndex) => {
              let k = `${dIndex + 1}_${qIndex + 1}_${subqIndex + 1}`;
              return this.mergeObjData(subq, cacheData[k] || {}, mergeReverse);
            });
          }
          return nq;
        });
        return detail;
      });
    },
    isNull(val) {
      if (val) {
        if (val === "[]") return true;
        if (objTypeOf(val) === "array" && !val.length) return true;
      }
      return val === null || val === "" || val === undefined;
    },
    mergeObjData(targetObj, cacheObj, mergeReverse) {
      let data = { ...targetObj };
      Object.keys(cacheObj).forEach((k) => {
        if (mergeReverse) {
          data[k] = this.isNull(cacheObj[k]) ? targetObj[k] : cacheObj[k];
        } else {
          data[k] = this.isNull(targetObj[k]) ? cacheObj[k] : targetObj[k];
        }
      });
      return data;
    },
    getImportPaperData() {
      if (!this.$refs.QuestionImportPaperEdit) return [];
      let paperData = deepCopy(this.$refs.QuestionImportPaperEdit.getData());
      const transformFieldMap = { body: "quesBody", options: "quesOptions" };
      const fields = Object.keys(transformFieldMap);
      const course = {
        id: this.data.importData.courseId,
        name: this.data.importData.courseName,
      };

      const transformQuestion = (question) => {
        question.id = null;
        question.course = course;
        fields.forEach((field) => {
          question[transformFieldMap[field]] = question[field];
          delete question[field];
        });
        if (question.quesOptions && question.quesOptions.length) {
          question.quesOptions = question.quesOptions.map((option) => {
            option.optionBody = option.body;
            delete option.body;
            return option;
          });
        }
        return question;
      };

      const detailInfo = paperData.map((detail) => {
        const questionInfo = detail.questions.map((question) => {
          transformQuestion(question);

          if (question.subQuestions && question.subQuestions.length) {
            question.subQuestions = question.subQuestions.map((subq) =>
              transformQuestion(subq)
            );
            question.score = calcSum(
              question.subQuestions.map((q) => q.score || 0)
            );
          }
          return question;
        });

        return {
          name: detail.name,
          number: detail.number,
          questionCount: questionInfo.length,
          questionInfo,
          questionScore: detail.questionScore,
          totalScore: calcSum(questionInfo.map((q) => q.score || 0)),
        };
      });
      // console.log(detailInfo);
      return detailInfo;
    },
    checkImportPaperData(paperData) {
      this.$message.closeAll();

      // 题目内容校验
      const MATCHING_QUESTION = ["PARAGRAPH_MATCHING", "BANKED_CLOZE"];
      const SELECT_QUESTION = [
        "SINGLE_ANSWER_QUESTION",
        "MULTIPLE_ANSWER_QUESTION",
        ...MATCHING_QUESTION,
      ];
      const NESTED_QUESTION = [
        ...MATCHING_QUESTION,
        "READING_COMPREHENSION",
        "CLOZE",
      ];
      let errInfos = [];
      paperData.forEach((detail) => {
        detail.questionInfo.forEach((question) => {
          const { questionType, quesBody } = question;
          const questionTitle = `第${detail.number}大题第${question.number}小题`;
          let qErrInfo = [];
          // 题干
          if (
            !MATCHING_QUESTION.includes(questionType) &&
            (!quesBody || isAnEmptyRichText(quesBody))
          ) {
            qErrInfo.push(`没有题干`);
          }

          // 选项
          if (SELECT_QUESTION.includes(questionType)) {
            if (!question.quesOptions.length) {
              qErrInfo.push(`没有选项`);
            }
            if (
              question.quesOptions.some((option) =>
                isAnEmptyRichText(option.optionBody)
              )
            ) {
              qErrInfo.push(`有选择内容为空`);
            }
          }

          // 小题数
          if (
            NESTED_QUESTION.includes(questionType) &&
            !question.subQuestions.length
          ) {
            qErrInfo.push(`没有小题`);
          }

          if (qErrInfo.length) {
            errInfos.push(`${questionTitle}${qErrInfo.join("、")}`);
            qErrInfo = [];
          }

          // 选词填空、段落匹配，单用模式时校验输入答案是否重复
          if (
            MATCHING_QUESTION.includes(questionType) &&
            question.quesParam.matchingMode === 1
          ) {
            let selectedAnswer = [],
              errorQuestionIndexs = [];
            question.subQuestions.forEach((subq, sindex) => {
              if (selectedAnswer.includes(subq.quesAnswer)) {
                errorQuestionIndexs.push(`${question.number}-${sindex + 1}`);
              } else {
                if (subq.quesAnswer !== "[]")
                  selectedAnswer.push(subq.quesAnswer);
              }
            });
            if (errorQuestionIndexs.length) {
              errInfos.push(
                `第${
                  detail.number
                }大题${errorQuestionIndexs.join()}小题答案重复！`
              );
            }
          }

          if (!NESTED_QUESTION.includes(questionType)) return;

          // 套题小题校验
          question.subQuestions.forEach((subq, sindex) => {
            const subqTitle = `第${detail.number}大题第${question.number}-${
              sindex + 1
            }小题`;
            if (
              questionType === "READING_COMPREHENSION" &&
              (!subq.quesBody || isAnEmptyRichText(subq.quesBody))
            ) {
              qErrInfo.push(`没有题干`);
            }
            if (
              SELECT_QUESTION.includes(subq.subqType) &&
              !MATCHING_QUESTION.includes(questionType)
            ) {
              if (!subq.quesOptions.length) {
                qErrInfo.push(`没有选项`);
              }
              if (
                subq.quesOptions.some((option) =>
                  isAnEmptyRichText(option.optionBody)
                )
              ) {
                qErrInfo.push(`有选择内容为空`);
              }
            }

            if (qErrInfo.length) {
              errInfos.push(`${subqTitle}${qErrInfo.join("、")}`);
              qErrInfo = [];
            }
          });
        });
      });
      if (errInfos.length) {
        this.$message({
          showClose: true,
          message: errInfos.join("。"),
          type: "error",
          duration: 0,
        });
        return;
      }

      if (!this.data.importData.useOriginalPaper) return true;

      let detailNumbers = paperData.map((detail) => detail.number);
      // 大题号重复性校验
      let repeatDetaiNumbers = [];
      let detailNums = [];
      for (let i = 0; i < detailNumbers.length; i++) {
        const num = detailNumbers[i];
        if (detailNums.includes(num)) {
          if (!repeatDetaiNumbers.includes(num)) repeatDetaiNumbers.push(num);
        } else {
          detailNums.push(num);
        }
      }
      if (repeatDetaiNumbers.length) {
        this.$message({
          showClose: true,
          message: `大题号${repeatDetaiNumbers.join("、")}重复`,
          type: "error",
          duration: 0,
        });
        return;
      }
      // 大题号连续性校验
      for (let i = 0; i < detailNumbers.length; i++) {
        if (detailNumbers[i] - 1 !== i) {
          this.$message({
            showClose: true,
            message: "大题号不连续",
            type: "error",
            duration: 0,
          });
          return;
        }
      }

      // 答案、分数校验
      let totalScore = calcSum(paperData.map((d) => d.totalScore));
      let errQuestions = [];
      paperData.forEach((detail) => {
        detail.questionInfo.forEach((question) => {
          if (question.subQuestions && question.subQuestions.length) {
            let subIndexs = [];
            question.subQuestions.forEach((subq, sind) => {
              if (!subq.score)
                subIndexs.push(question.number + "-" + (sind + 1));
            });
            if (subIndexs.length)
              errQuestions.push(
                `第${detail.number}大题第${subIndexs.join()}小题`
              );
          } else {
            if (!question.score) {
              errQuestions.push(
                `第${detail.number}大题第${question.number}小题`
              );
            }
          }
        });
      });
      if (errQuestions.length) {
        this.$message({
          showClose: true,
          message: `请设置如下试题的分值：${errQuestions.join("、")}。`,
          type: "error",
          duration: 0,
        });
        return;
      }

      if (
        this.data.importData.checkTotalScore &&
        totalScore !== this.data.importData.totalScore
      ) {
        this.$message({
          showClose: true,
          message: `试卷总分与导入设置的总分不一致！`,
          type: "error",
          duration: 0,
        });
        return;
      }

      return true;
    },
    async confirm() {
      const confirm = await this.$confirm("确认加入题库吗?", "提示", {
        type: "warning",
      }).catch(() => {});
      if (confirm !== "confirm") return;

      const detailInfo = this.getImportPaperData();
      if (!this.checkImportPaperData(detailInfo)) return;

      if (this.loading) return;
      this.loading = true;

      const res = await questionImportPaperSave({
        ...this.data.importData,
        detailInfo,
      }).catch(() => {});

      this.loading = false;
      if (!res) return;

      this.$message.success("提交成功！");
      this.$emit("modified");
      this.cancel();
    },
    // 导入答案属性
    toImportAnswer() {
      const detailInfo = this.getImportPaperData();
      this.uploadAnswerData = {
        detailInfo: JSON.stringify(detailInfo),
        ...this.data.importData,
      };
      this.$refs.ImportAnswerDialog.open();
    },
    async answerTemplateDownload() {
      const detailInfo = this.getImportPaperData();

      const res = await downloadByApi(() => {
        return questionImportDownloadTemplate({
          detailInfo,
          ...this.data.importData,
        });
      }).catch((e) => {
        this.$message.error(e || "下载失败，请重新尝试！");
      });

      if (!res) return;
      this.$message.success("下载成功！");
    },
    answerUploaded(res) {
      const cacheData = this.getCachePaperInfo(
        res.data.detailInfo,
        questionInfoField
      );
      this.paperData = this.assignCachePaperData(
        this.paperData,
        cacheData,
        true
      );
      this.questionKey = randomCode();
    },
  },
};
</script>
