<script setup lang="ts">
import {
  offlineExamUploadFileTypesApi,
  offlineExamBatchSubmitPaperApi,
} from "@/api/offlineExam";
import { OfflineExam } from "@/types/student-client";
import { computed } from "vue";
import { CloudUpload, EllipsisHorizontalCircle, Add } from "@vicons/ionicons5";
import { UploadInst, UploadFileInfo } from "naive-ui";
import { fileFormatCheck } from "./fileFormatCheck";
import { fileMD5 } from "@/utils/md5";

const props = defineProps<{
  course: OfflineExam;
}>();
const emit = defineEmits<{
  (e: "modified"): void;
}>();

let modalIsShow = $ref(false);
function close() {
  modalIsShow = false;
}
function open() {
  modalIsShow = true;
}
function modalOpened() {
  uploadFileList = [];
  loading = false;
  void getUploadFileTypes();
}

interface FileChangeOption {
  file: UploadFileInfo;
  fileList: Array<UploadFileInfo>;
  event?: Event;
}

// upload option
const maxUploadImageCount = 6;
const UPLOAD_FILE_ACCEPT_TYPE: Record<string, string> = {
  ZIP: ".zip,zip",
  PDF: ".pdf",
  IMAGE: ".jpeg,.jpg,.png",
};
const isImageType = computed(() => selectedFileType === "IMAGE");
const uploadListType = computed(() =>
  isImageType.value ? "image-card" : "text"
);
const uploadFileAccept = computed(
  () => UPLOAD_FILE_ACCEPT_TYPE[selectedFileType]
);
const uploadFileMaxCount = computed(() =>
  isImageType.value ? maxUploadImageCount : 1
);
const uploadFileMaxSize = computed(() => {
  return isImageType.value ? 5 : 30;
});

const UploadRef = $ref<UploadInst | null>(null);
let uploadFileList = $ref<UploadFileInfo[]>([]);
let loading = $ref(false);
let selectedFileType = $ref("");
let serverFileTypes = $ref<string[]>([]);
async function getUploadFileTypes() {
  if (!props.course.examId) return;
  const res = await offlineExamUploadFileTypesApi(props.course.examId);

  serverFileTypes = res.data.OFFLINE_UPLOAD_FILE_TYPE
    ? JSON.parse(res.data.OFFLINE_UPLOAD_FILE_TYPE)
    : [];
  selectedFileType = serverFileTypes[0];
}
function fileTypeChange() {
  uploadFileList = [];
}

async function fileChange(options: FileChangeOption) {
  // console.log(options.file, options.fileList);
  if (options.file.status !== "pending") return;

  // upload组件事件触发顺序：change,update:fileList
  // update:fileList触发之时，会修改uploadFileList。
  // 想要控制校验不合格的文件显示，只能异步校验后，删除校验不合格的文件。
  const valid = await checkFile(options.file).catch(() => false);
  const fileIndex = uploadFileList.findIndex(
    (item) => item.name === options.file.name
  );
  if (fileIndex !== -1) {
    uploadFileList.splice(fileIndex, 1);
  }
  console.log(valid);
  if (!valid) return;
  uploadFileList.push(options.file);
}

async function checkFile(file: UploadFileInfo) {
  // size
  const fileMaxSize = uploadFileMaxSize.value * 1024 * 1024;
  if (file.file && file.file.size > fileMaxSize) {
    const msg = `${file.name}太大，作答文件不能超过${uploadFileMaxSize.value}MB！`;
    $message.error(msg);
    return Promise.reject(msg);
  }

  // suffix format
  const suffix = file.name.split(".").pop() || "";
  if (suffix && suffix.toLowerCase() !== suffix) {
    const msg = `${file.name}文件名后缀必须是小写！`;
    $message.error(msg);
    return Promise.reject(msg);
  }

  // strict format
  let errorMsg = "";
  const result = await fileFormatCheck(
    file.file as File,
    selectedFileType
  ).catch((error: string) => {
    errorMsg = error;
    $message.error(error);
  });
  if (!result) return Promise.reject(errorMsg);

  return Promise.resolve(true);
}

function dialogConfirm(content: string) {
  return new Promise((resolve, reject) => {
    $dialog.create({
      content,
      maskClosable: false,
      closable: false,
      closeOnEsc: false,
      negativeText: "取消",
      positiveText: "确定",
      onPositiveClick: () => {
        resolve("confirm");
      },
      onNegativeClick: () => {
        reject("cancel");
      },
    });
  });
}

async function toSubmit() {
  if (loading) return;

  // check
  if (!selectedFileType) {
    return $message.error("请先选择上传文件类型！");
  }

  if (!uploadFileList.length) return $message.error("请先选择上传文件！");

  const checkFuncs = uploadFileList.map((item) => checkFile(item));
  const fileValid = await Promise.all(checkFuncs).catch(() => [false]);

  if (fileValid.some((item) => !item)) return;

  if (props.course.offlineFiles && props.course.offlineFiles.length) {
    const confirm = await dialogConfirm("已有作答附件，是否覆盖？").catch(
      (error: string) => error
    );
    if (confirm !== "confirm") return;
  }

  // upload
  loading = true;
  let params = new FormData();
  params.append("examRecordDataId", props.course.examRecordDataId + "");
  params.append("fileType", selectedFileType);

  for (const file of uploadFileList) {
    params.append("fileArray", file.file as Blob);
    if (file.file) {
      const fileMd5Str = await fileMD5(file.file);
      params.append("fileMd5Array", fileMd5Str);
    }
  }

  const res = await offlineExamBatchSubmitPaperApi(params).catch(() => {
    logger({
      cnl: ["local", "server"],
      pgn: "离线考试",
      act: "上传离线考试附件",
      stk: "",
      dtl: "上传离线考试附件失败",
    });
  });
  loading = false;
  if (!res) return;

  $message.success("上传成功！");
  close();
  emit("modified");
}

// expose
defineExpose({
  open,
});
</script>

<template>
  <n-modal
    v-model:show="modalIsShow"
    preset="dialog"
    title="上传文件"
    :showIcon="false"
    :maskClosable="false"
    :closeOnEsc="false"
    class="qm-modal"
    style="width: 600px"
    :onAfterEnter="modalOpened"
  >
    <div>
      <n-form
        size="medium"
        labelPlacement="left"
        labelWidth="120px"
        :showFeedback="false"
      >
        <n-form-item label="课程名称：">
          <p class="course-name">{{ course.courseName }}</p>
        </n-form-item>
        <n-form-item class="qm-mb-10" label="上传文件类型：">
          <n-radio-group
            v-model:value="selectedFileType"
            name="radiogroup"
            @update:value="fileTypeChange"
          >
            <n-space>
              <n-radio
                v-for="item in serverFileTypes"
                :key="item"
                :value="item"
              >
                {{ item === "IMAGE" ? "图片" : item }}
              </n-radio>
            </n-space>
          </n-radio-group>
        </n-form-item>
        <n-form-item label="上传文件：">
          <n-upload
            ref="UploadRef"
            v-model:fileList="uploadFileList"
            :accept="uploadFileAccept"
            :disabled="loading"
            :listType="uploadListType"
            :multiple="isImageType"
            :max="uploadFileMaxCount"
            :defaultUpload="false"
            @change="fileChange"
          >
            <n-icon v-if="isImageType" :component="Add" size="26"></n-icon>
            <n-button v-else type="success">选择文件</n-button>
          </n-upload>
        </n-form-item>
      </n-form>
    </div>

    <template #action>
      <n-button
        type="success"
        :disabled="!uploadFileList.length || loading"
        :loading="loading"
        @click="toSubmit"
      >
        <template #icon>
          <n-icon :component="CloudUpload"></n-icon>
        </template>
        确认上传</n-button
      >
      <n-button type="success" @click="close">
        <template #icon>
          <n-icon :component="EllipsisHorizontalCircle"></n-icon>
        </template>
        取消上传</n-button
      >
    </template>
  </n-modal>
</template>

<style scoped>
.course-name {
  color: var(--app-color-error);
  font-size: var(--app-font-size-large);
}
</style>
