<script setup lang="ts">
import QuestionBody from "./QuestionBody.vue";
import VueQrcode from "@chenfengyuan/vue-qrcode";
import { store } from "@/store/store";
import { onUnmounted, watch } from "vue";
import { httpApp } from "@/plugins/axiosApp";
import UploadPhotos from "./UploadPhotos.vue";
import { Checkmark } from "@vicons/ionicons5";

let isShowAnswer = $ref(false);
function toggleShowAnswer() {
  isShowAnswer = !isShowAnswer;
}

const examRecordDataId = store.exam.examRecordDataId;
const order = store.exam.currentQuestion.order;
const examQuestion = $computed(() => store.exam.currentQuestion);
const rightAnswerTransform = $computed(() =>
  examQuestion.rightAnswer?.join("")
);

const canAttachPhotos = $computed(
  () => store.exam.WEIXIN_ANSWER_ENABLED && !isAudioAnswerType
);

const isAudioAnswerType = $computed(
  () => store.exam.currentQuestion.answerType === "SINGLE_AUDIO"
);

const shouldFetchQrCode = $computed(() => store.exam.WEIXIN_ANSWER_ENABLED);

let studentAnswer = $ref("");
let originalStudentAnswer: string | null = $ref(null);

watch(
  () => store.exam.currentQuestion,
  () => {
    if (store.exam.currentQuestion) {
      studentAnswer = store.exam.currentQuestion.studentAnswer || "";
      originalStudentAnswer = store.exam.currentQuestion.studentAnswer;
    }
  },
  { immediate: true }
);

watch(
  () => studentAnswer,
  () => {
    let realAnswer = undefined;
    if (studentAnswer) {
      // 如果有实际内容
      realAnswer = studentAnswer
        .replace(/<sup><\/sup>/gi, "")
        .replace(/<sub><\/sub>/gi, "")
        .replace(/<script/gi, "&lt;script")
        .replace(/script>/gi, "script&gt;");
    }
    if (realAnswer !== store.exam.currentQuestion.studentAnswer) {
      store.updateExamQuestion({
        order: examQuestion.order,
        studentAnswer: realAnswer,
      });
    }
  }
);

const answerDiv: HTMLDivElement = $ref();
const answerWordCount = $computed(() => {
  if (studentAnswer && answerDiv) {
    return answerDiv.innerText.replace(/\s+/g, "").length;
  } else {
    const ele = document.createElement("div");
    ele.innerHTML = studentAnswer;

    return ele.innerText.replace(/\s+/g, "").length;
  }
});

function disableCtrl(e: KeyboardEvent) {
  if (e.ctrlKey || e.metaKey || e.altKey) {
    // .ctrlKey tells that ctrl key was pressed.
    e.preventDefault();
    return false;
  }
  return true;
}

let lastInputTime = Date.now();
function textInput($event: Event) {
  // 对 input 事件进行节流
  if ($event instanceof InputEvent) {
    if (Date.now() - lastInputTime < 1.5 * 1000) {
      return;
    } else {
      lastInputTime = Date.now();
    }
  }
  // console.log("begin", $event, studentAnswer);
  const sDom = document.createElement("div");
  sDom.innerHTML = studentAnswer;
  const photoDom = sDom.querySelector(".photo-answers-block");
  const photoStr = photoDom?.outerHTML ?? "";
  // console.log({ photoStr });

  const answerDom = <HTMLDivElement>$event.target;
  const photoOfAnswerDom = answerDom.querySelector(".photo-answers-block");
  photoOfAnswerDom && answerDom.removeChild(photoOfAnswerDom);

  studentAnswer = answerDom.innerHTML + photoStr;
  // console.log("end textInput");
}

let copyNode: DocumentFragment;
function textCopy() {
  const selElm = getSelection();
  if (!selElm) return;
  var selRange = selElm.getRangeAt(0);
  copyNode = selRange.cloneContents();
}

function textCut() {
  const selElm = getSelection();
  if (!selElm) return;
  const selRange = selElm.getRangeAt(0);
  copyNode = selRange.extractContents();
  studentAnswer = answerDiv.innerHTML;
}

function textPaste() {
  const selElm = getSelection();
  if (!selElm) return;
  const selRange = selElm.getRangeAt(0);
  selRange.deleteContents();
  selRange.insertNode(copyNode.cloneNode(true));
  studentAnswer = answerDiv.innerHTML;
}

function textSup() {
  const origHMTL = answerDiv.innerHTML + "";
  getSelection()?.getRangeAt(0).surroundContents(document.createElement("sup"));
  if (answerDiv.querySelector("sup sup, sub sub, sup sub, sub sup")) {
    console.log("不允许多层上标下标");
    answerDiv.innerHTML = origHMTL;
  } else {
    studentAnswer = answerDiv.innerHTML;
  }
}

function undoTextSup() {
  const selElm = getSelection();
  if (!selElm) return;
  // 取消上标时，必须选择之前的文字，才能实现
  // @ts-expect-error 非web标准方法
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  selElm.modify("extend", "left", "character");
  let selRange = selElm.getRangeAt(0);
  const documentFragment = selRange.extractContents();
  if (documentFragment.textContent) {
    const text = new Text(documentFragment.textContent);
    selRange.insertNode(text);
    studentAnswer = answerDiv.innerHTML;
  }
}

function textSub() {
  const origHMTL = answerDiv.innerHTML + "";
  getSelection()?.getRangeAt(0).surroundContents(document.createElement("sub"));
  if (answerDiv.querySelector("sup sup, sub sub, sup sub, sub sup")) {
    console.log("不允许多层上标下标");
    answerDiv.innerHTML = origHMTL;
  } else {
    studentAnswer = answerDiv.innerHTML;
  }
}

let qrValue = $ref("");
let qrScanned = $ref(false);

let hasUnmounted = false;
async function fetchQRCode() {
  if (shouldFetchQrCode && !qrValue) {
    const transferFileType =
      examQuestion.answerType === "SINGLE_AUDIO" ? "AUDIO" : "PIC";

    try {
      const response = await httpApp.post<string>(
        "/api/ecs_oe_student/examControl/getQrCode",
        {
          examRecordDataId,
          order: examQuestion.order,
          transferFileType,
          testEnv: false,
        },
        {
          "axios-retry": {
            retries: 10,
            retryDelay: () => 3 * 1000,
            retryCondition: () => !hasUnmounted,
          },
          noErrorMessage: true,
        }
      );
      let origin = window.location.origin.replace(
        /.*(\.(ea100.com.cn|exam-cloud.cn))/,
        `${location.protocol}//www$1`
      );
      console.debug("测试验证:", origin);
      if (import.meta.env.DEV) {
        origin = import.meta.env.VITE_CONFIG_API_SERVER as string;
      }
      qrValue = response.data + encodeURIComponent("&apiServer=" + origin);
    } catch (error) {
      logger({
        cnl: ["server"],
        pgu: "AUTO",
        act: "获取简答题二维码出错",
        possibleError: error,
      });
    }
  }
}
onUnmounted(() => {
  hasUnmounted = true;
});

watch(
  () => store.exam.questionQrCodeScanned,
  () => {
    if (store.exam.questionQrCodeScanned.order === examQuestion.order) {
      qrScanned = true;
    }
  }
);

let pictureAnswer: {
  transferFileType: string;
  order: number;
  fileUrl: string;
} = $ref();
let uploadModalVisible = $ref(false);
watch(
  () => store.exam.questionAnswerFileUrl,
  (value) => {
    for (const q of value) {
      if (!q?.saved) {
        let acknowledgeStatus = "CONFIRMED";

        // 目前只针对音频题有丢弃的可能
        if (
          q.transferFileType === "PIC" &&
          (q.order != order || !uploadModalVisible)
        ) {
          acknowledgeStatus = "DISCARDED";
        }
        httpApp
          .post(
            "/api/ecs_oe_student/examControl/saveUploadedFileAcknowledgeStatus",
            {
              examRecordDataId,
              filePath: q.fileUrl,
              order: q.order,
              acknowledgeStatus,
            }
          )
          .then(() => {
            if (q.transferFileType === "AUDIO") {
              store.updateExamQuestion({
                order: q.order,
                studentAnswer: q.fileUrl,
              });
            } else if (
              acknowledgeStatus === "CONFIRMED" &&
              q.transferFileType === "PIC"
            ) {
              pictureAnswer = q;
            }
            q.saved = true;
            if (acknowledgeStatus === "CONFIRMED") {
              $message.info("小程序作答已更新");
            }
            logger({
              cnl: ["server"],
              act: "小程序作答获得更新",
              ext: {
                acknowledgeStatus,
                transferFileType: q.transferFileType,
              },
            });
          })
          .catch(() => {
            $message.error("更新小程序答案失败！");
          });
      }
    }
  }
);

watch(
  () => store.exam.currentQuestion?.order,
  async () => {
    if (!store.exam.WEIXIN_ANSWER_ENABLED) return;
    if (store.exam.currentQuestion?.questionType !== "ESSAY") return;

    await fetchQRCode();
  },
  { immediate: true }
);

let photoAnswers: string[] = $computed({
  get() {
    if (!studentAnswer) return [];
    const ele = document.createElement("div");
    ele.innerHTML = studentAnswer;
    const imgs = ele.querySelectorAll(
      ".photo-answer"
    ) as unknown as HTMLImageElement[];
    return [...imgs].map((e) =>
      e.src.replace("?x-oss-process=image/resize,m_lfit,h_200,w_200", "")
    );
  },
  set(pSrcs) {
    let imageStr = pSrcs.map(
      (v) =>
        `<a href='${v}' target='_blank' ><img class='photo-answer' src='${
          v + "?x-oss-process=image/resize,m_lfit,h_200,w_200"
        }' /></a>`
    );
    const ele = document.createElement("div");
    ele.innerHTML = studentAnswer || "";
    const pEle = ele.querySelectorAll(".photo-answers-block");
    if (pEle) [...pEle].forEach((v) => v.remove());
    // console.log(ele.innerHTML);

    if (!ele.innerHTML && pSrcs.length === 0) {
      // 完全为空则重置答案
      studentAnswer = "";
      // 更新answerDiv的内容
      originalStudentAnswer = null;
    } else {
      studentAnswer =
        ele.innerHTML +
        `<div class='photo-answers-block'>${imageStr.join("")}</div>`;
      // 更新answerDiv的内容
      originalStudentAnswer = studentAnswer;
    }
  },
});

function photoRemoved(url: string) {
  photoAnswers = photoAnswers.filter((v) => v !== url);
}
function photosReseted(urls: string[]) {
  photoAnswers = [...urls];
}
</script>

<template>
  <div class="question-view">
    <question-body :questionBody="examQuestion.body"></question-body>
    <div class="ops">
      <div class="score">({{ examQuestion.questionScore }}分)</div>
    </div>
    <div class="option">
      <div v-if="!isAudioAnswerType">
        <div class="menu">
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="textCopy"
          >
            复制
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="textCut"
          >
            剪切
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="textPaste"
          >
            粘贴
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="textSup"
          >
            上标
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="undoTextSup"
          >
            取消上标
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="textSub"
          >
            下标
          </n-button>
          <n-button
            type="success"
            class="text-ops"
            size="small"
            @click="undoTextSup"
          >
            取消下标
          </n-button>
        </div>
        <div
          v-once
          ref="answerDiv"
          ondragstart="return false"
          ondrop="return false"
          :contenteditable="true"
          class="stu-answer"
          @keydown="disableCtrl"
          @input="($event) => textInput($event)"
          @blur="($event) => textInput($event)"
          v-html="originalStudentAnswer"
        />
        <div
          style="
            margin-top: -25px;
            margin-bottom: 25px;
            width: 100%;
            max-width: 500px;
          "
        >
          <div style="float: right; margin-right: 10px">
            {{ answerWordCount }}
          </div>
        </div>
      </div>
      <div v-if="shouldFetchQrCode && isAudioAnswerType">
        <div>
          <div v-if="qrValue" style="display: flex">
            <VueQrcode
              :value="qrValue"
              :options="{ width: 200 }"
              style="margin-left: -10px"
            />
            <div style="margin-top: 10px">
              <div>
                请使用<span style="font-weight: 900; color: #1e90ff">微信</span
                >扫描二维码后，在微信小程序上{{
                  isAudioAnswerType ? "录音" : "拍照"
                }}，并上传文件。
              </div>
              <div
                v-if="qrScanned"
                class="tw-flex tw-items-center"
                style="margin-top: 30px; font-size: 30px"
              >
                {{ examQuestion.studentAnswer ? "已上传" : "已扫描" }}
                <n-icon :component="Checkmark" />
              </div>
            </div>
          </div>
          <div v-else>正在获取二维码...</div>
        </div>

        <div
          class="audio-answer audio-answer-line-height"
          style="margin-top: 20px"
        >
          <span class="audio-answer-line-height">答案：</span>
          <audio
            v-if="examQuestion.studentAnswer"
            class="audio-answer-line-height"
            controls
            controlsList="nodownload"
            :src="examQuestion.studentAnswer"
          />
          <span v-else class="audio-answer-line-height">未上传文件</span>
        </div>
      </div>

      <div v-if="canAttachPhotos" style="padding-top: 1px">
        <UploadPhotos
          v-model:uploadModalVisible="uploadModalVisible"
          :defaultList="photoAnswers.map((v) => v)"
          :qrValue="qrValue"
          style="margin-top: 20px; width: 350px"
          :pictureAnswer="pictureAnswer"
          @onPhotoRemoved="photoRemoved"
          @onPhotosReseted="photosReseted"
        />
      </div>
      <div class="reset" style="padding-top: 20px">
        <span v-if="store.examShouldShowAnswer">
          <n-button type="success" @click="toggleShowAnswer">
            {{ isShowAnswer ? "隐藏" : "显示" }}答案
          </n-button>
        </span>
        <div v-if="isShowAnswer" class="tw-mt-2">
          正确答案：
          <div class="right-answer-section" v-html="rightAnswerTransform"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.question-view {
  display: grid;
  grid-row-gap: 10px;
}

.question-body {
  font-size: 18px;
  /* margin-bottom: 10px; */
}

.ops {
  display: flex;
  align-items: flex-end;
}

.text-ops {
  margin: 0 5px 5px 0;
}

.stu-answer {
  width: 100%;
  max-width: 500px;
  min-height: 300px;
  border: 1px solid grey;
  padding: 4px;
}

.audio-answer {
  font-size: 24px;
  line-height: 54px;
}
.audio-answer-line-height {
  line-height: 54px;
  vertical-align: text-bottom;
}
</style>
<style>
.photo-answers-block {
  display: none;
}
</style>
