<script setup lang="ts">
import {
  examQuestionDataApi,
  examRecordDataApi,
  examRecordPaperStructApi,
  examRecordQuestionsApi,
  onlinePracticeTypeApi,
} from "@/api/onlinePractice";
import {
  ExamQuestion,
  PaperStruct,
  QuestionWrapperItem,
} from "@/types/student-client";
import { toChineseNumber } from "@/utils/utils";
import { onMounted } from "vue";
import { Close, Checkmark } from "@vicons/ionicons5";

const props = defineProps<{
  examId: string | number;
  examRecordDataId: string | number;
  fromCache: boolean;
  courseCode: string;
}>();
const emit = defineEmits<{
  (e: "ready"): void;
}>();

let questionGroupList = $ref<PaperStruct["defaultPaper"]["questionGroupList"]>(
  []
);
let practiceType = $ref("");

async function initData() {
  const ptRes = await onlinePracticeTypeApi(props.examId);
  practiceType = ptRes.data.PRACTICE_TYPE;

  const { paperStruct, examQuestionList, examRecordData } =
    await getPaperData();
  if (!paperStruct || !examQuestionList) {
    $message.error("获取试卷信息失败", {
      duration: 15,
      closable: true,
    });
    return;
  }

  const examQuestionMap = await getQuestionWrapperMapData(
    examQuestionList,
    examRecordData.examRecord.paperType
  );

  questionGroupList = paperStruct.defaultPaper.questionGroupList.map(
    (questionGroup) => {
      questionGroup.questionWrapperList = questionGroup.questionWrapperList.map(
        (question) => {
          return examQuestionMap[question.questionId];
        }
      );
      return questionGroup;
    }
  );
}

async function getPaperData() {
  type FetchDataFuncType = [
    Promise<{ data: PaperStruct }>,
    Promise<{
      data: { examQuestionEntities: ExamQuestion[] };
    }>,
    Promise<{
      data: { examRecord: { paperType: string } };
    }>
  ];
  const fetchDataFunc: FetchDataFuncType = [
    examRecordPaperStructApi(props.examRecordDataId, props.fromCache),
    examRecordQuestionsApi(props.examRecordDataId, props.fromCache),
    examRecordDataApi(props.examRecordDataId, props.fromCache),
  ];

  const [paperStructRes, examQuestionListRes, examRecordDataRes] =
    await Promise.all(fetchDataFunc);

  const [paperStruct, examQuestionList, examRecordData] = [
    paperStructRes.data,
    examQuestionListRes.data.examQuestionEntities,
    examRecordDataRes.data,
  ];
  return {
    paperStruct,
    examQuestionList,
    examRecordData,
  };
}

async function getQuestionWrapperMapData(
  examQuestionList: ExamQuestion[],
  groupCode: string
) {
  let examQuestionMap: Record<string, ExamQuestion[]> = {};
  examQuestionList.forEach((q) => {
    if (!examQuestionMap[q.questionId]) examQuestionMap[q.questionId] = [];
    examQuestionMap[q.questionId].push(q);
  });

  const fetchFunc = Object.keys(examQuestionMap).map((questionId) =>
    examQuestionDataApi({
      questionId,
      examId: props.examId,
      courseCode: props.courseCode,
      groupCode,
    })
  );

  const questionList = await Promise.all(fetchFunc);

  let examQuestionWrapperMap: Record<string, QuestionWrapperItem> = {};
  questionList.forEach((qItem) => {
    const q = qItem.data;
    const questionList = examQuestionMap[q.id].map((questionInfo, index) => {
      return Object.assign(
        {},
        questionInfo,
        q.masterVersion.questionUnitList[index]
      ) as ExamQuestion;
    });

    examQuestionWrapperMap[q.id] = Object.assign(
      {},
      { examQuestionList: questionList, questionId: q.id },
      q.masterVersion
    );
  });

  return examQuestionWrapperMap;
}

function restoreAudio(str: string) {
  return (str || "")
    .replace(/<a/g, "<audio controls ")
    .replace(/url=/g, "src=")
    .replace(/a>/g, "audio>");
}
const optionName = "ABCDEFGHIJ".split("");
function indexToABCD(index: number) {
  return optionName[index];
}
function parseRightAnswer(
  questionType: string,
  rightAnswer: string[] | undefined,
  optionPermutation: number[] | undefined
) {
  if (!rightAnswer) return "";
  // 选择题答案是非乱序的真实答案，展示时要转成乱序的题目答案。
  if (["SINGLE_CHOICE", "MULTIPLE_CHOICE"].includes(questionType)) {
    const permutationOptions = optionPermutation as number[];
    const permutationAnswer = rightAnswer
      .map((answer) => permutationOptions.indexOf(Number(answer)))
      .sort();
    return permutationAnswer.map((ans) => indexToABCD(ans)).join("");
  } else if (["TRUE_OR_FALSE"].includes(questionType)) {
    return { true: "正确", false: "错误" }[rightAnswer.join("")];
  } else {
    return rightAnswer.join("");
  }
}
function parseStudentAnswer(
  questionType: string,
  studentAnswer: string,
  optionPermutation: number[] | undefined
) {
  if (!studentAnswer) return "";

  // 选择题答案是非乱序的真实答案，展示时要转成乱序的题目答案。
  if (["SINGLE_CHOICE", "MULTIPLE_CHOICE"].includes(questionType)) {
    const permutationOptions = optionPermutation as number[];
    const permutationAnswer = studentAnswer
      .split("")
      .map((answer) => permutationOptions.indexOf(Number(answer)))
      .sort();
    return permutationAnswer.map((ans) => indexToABCD(ans)).join("");
  } else if (["TRUE_OR_FALSE"].includes(questionType)) {
    return { true: "正确", false: "错误" }[studentAnswer];
  } else {
    return studentAnswer;
  }
}
function equalAnswer(
  questionType: string,
  studentAnswer: string,
  rightAnswer: string[] | undefined
) {
  if (!rightAnswer) return null;
  if (["FILL_UP", "ESSAY"].includes(questionType)) return null;

  return studentAnswer === rightAnswer.join("");
}
function checkIsObjective(questionType: string) {
  return ["SINGLE_CHOICE", "MULTIPLE_CHOICE", "TRUE_OR_FALSE"].includes(
    questionType
  );
}

onMounted(async () => {
  await initData().catch(() => false);
  emit("ready");
});
</script>

<template>
  <div class="exam-paper">
    <div
      v-for="(group, gindex) in questionGroupList"
      :key="gindex"
      class="question-group"
    >
      <h3 class="group-title">
        {{ toChineseNumber(gindex + 1) }}、{{ group.groupName }}（{{
          group.groupScore
        }}分）
      </h3>
      <div class="group-questions">
        <div
          v-for="questionWrapper in group.questionWrapperList"
          :key="questionWrapper.questionId"
          class="question-wrapper"
        >
          <div
            v-if="questionWrapper.body"
            class="question-wrapper-body"
            v-html="restoreAudio(questionWrapper.body)"
          ></div>
          <div
            v-for="question in questionWrapper.examQuestionList"
            :key="question.questionId"
            class="question-item"
          >
            <div class="question-item-title line-text">
              <span>{{ question.order }}、</span>
              <div
                class="question-item-title-content"
                v-html="restoreAudio(question.body)"
              ></div>
            </div>
            <!-- options -->
            <div
              v-if="
                question.questionOptionList &&
                question.questionOptionList.length
              "
              class="question-item-options"
            >
              <div
                v-for="(optionOrder, oindex) in question.optionPermutation"
                :key="optionOrder"
                class="question-item-option line-text"
              >
                <span>{{ indexToABCD(oindex) }}、</span>
                <div
                  class="question-item-option-content"
                  v-html="
                    restoreAudio(question.questionOptionList[optionOrder].body)
                  "
                ></div>
              </div>
            </div>
            <!-- right answer -->
            <div
              v-if="practiceType !== 'NO_ANSWER'"
              class="question-item-answer line-text"
            >
              <span>正确答案：</span>
              <div
                class="question-item-answer-content"
                v-html="
                  parseRightAnswer(
                    question.questionType,
                    question.rightAnswer,
                    question.optionPermutation
                  )
                "
              ></div>
            </div>
            <!-- student answer -->
            <div class="question-item-answer line-text">
              <span>学生答案：</span>
              <div
                class="question-item-answer-content"
                v-html="
                  parseStudentAnswer(
                    question.questionType,
                    question.studentAnswer,
                    question.optionPermutation
                  )
                "
              ></div>
              <div
                v-if="checkIsObjective(question.questionType)"
                class="question-item-answer-result"
              >
                <n-icon
                  v-if="
                    equalAnswer(
                      question.questionType,
                      question.studentAnswer,
                      question.rightAnswer
                    )
                  "
                  :component="Checkmark"
                  color="#13bb8a"
                  :size="16"
                ></n-icon>
                <n-icon
                  v-else
                  :component="Close"
                  color="#ed4014"
                  :size="16"
                ></n-icon>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.question-group {
  margin-bottom: 20px;
}
.group-title {
  font-size: var(--app-font-size-large);
  font-weight: 600;
  margin-bottom: 5px;
}
.question-item {
  margin-bottom: 10px;
}
.question-item-options {
  margin-bottom: 10px;
}

.line-text > * {
  display: inline-block;
  vertical-align: top;
}
.line-text audio {
  height: 32px;
}

.line-text .question-item-answer-result .n-icon {
  vertical-align: middle;
  margin-top: -2px;
}
</style>
<style>
.line-text audio {
  height: 32px;
}
.line-text img {
  display: inline-block;
  vertical-align: middle;
}
</style>
