<template>
  <a-upload
    :action="uploadUrl"
    :headers="headers"
    :accept="accept"
    :data="uploadDataDict"
    :show-upload-list="false"
    :custom-request="customRequest"
    :disabled="disabled || loading"
    :before-upload="handleBeforeUpload"
    @change="handleFileChange"
  >
    <slot>
      <a-button>
        <template #icon><ImportOutlined /></template>点击导入
      </a-button>
    </slot>
  </a-upload>
</template>

<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { DownloadOutlined, ImportOutlined } from "@ant-design/icons-vue";
import { message } from "ant-design-vue";
import type { UploadProps } from "ant-design-vue";

import type { AxiosError, AxiosProgressEvent } from "axios";
import { request } from "@/utils/request";
import { getFileMD5 } from "@/utils/crypto";

defineOptions({
  name: "ImportBtn",
});

const props = withDefaults(
  defineProps<{
    uploadUrl: string;
    format?: string[];
    uploadData?: Record<string, any>;
    maxSize?: number;
    disabled?: boolean;
    uploadFileAlias?: string;
  }>(),
  {
    uploadUrl: "",
    format: () => ["xls", "xlsx"],
    uploadData: () => {
      return {};
    },
    maxSize: 20 * 1024 * 1024,
    uploadFileAlias: "file",
    disabled: false,
  }
);

// interface ResultData {
//   success: boolean;
//   message: string;
// }
// type UploadSuccessData = ResultData & UploadResultType;
// const emit = defineEmits<{
//   uploading: [];
//   uploadError: [value: ResultData];
//   uploadSuccess: [value: UploadSuccessData];
//   validError: [value: ResultData];
// }>();

const emit = defineEmits([
  "uploading",
  "uploadError",
  "uploadSuccess",
  "validError",
]);

const uploadRef = ref();
const canUpload = ref(false);
const uploadDataDict = ref({});
const headers = ref({ md5: "" });
const result = ref({ success: true, message: "" });
const loading = ref(false);
const uploadProgress = ref(0);
const curFileUid = ref("");

const accept = computed(() => {
  return props.format.map((el) => `.${el}`).join();
});

function checkFileFormat(fileType: string) {
  const fileFormat = fileType.split(".").pop()?.toLocaleLowerCase();
  return props.format.some((item) => item.toLocaleLowerCase() === fileFormat);
}

const handleFileChange: UploadProps["onChange"] = ({ file, fileList }) => {
  if (!fileList.length) {
    curFileUid.value = "";
    result.value = {
      success: true,
      message: "",
    };
    return;
  }

  if (curFileUid.value !== file.uid) {
    result.value = {
      success: true,
      message: "",
    };
  }
  curFileUid.value = file.uid;
  canUpload.value = file.status === "uploading";
};

const handleBeforeUpload: UploadProps["beforeUpload"] = async (file) => {
  uploadDataDict.value = {
    ...props.uploadData,
    filename: file.name,
  };

  if (file.size > props.maxSize) {
    handleExceededSize();
    return Promise.reject(result.value);
  }

  if (!checkFileFormat(file.name)) {
    handleFormatError();
    return Promise.reject(result.value);
  }

  const md5 = await getFileMD5(file);
  headers.value.md5 = md5;

  loading.value = true;
};

interface UploadResultType {
  hasError: boolean;
  failRecords: Array<{
    msg: string;
    lineNum: number;
  }>;
}

const customRequest: UploadProps["customRequest"] = (option) => {
  const { file, data } = option;

  const formData = new FormData();
  const paramData: Record<string, any> = data || {};
  Object.entries(paramData).forEach(([k, v]) => {
    formData.append(k, v);
  });
  formData.append(props.uploadFileAlias, file as File);
  emit("uploading");

  (
    request({
      url: option.action as string,
      data: formData,
      headers: option.headers,
      onUploadProgress: (data: AxiosProgressEvent) => {
        uploadProgress.value = data.total
          ? Math.floor((100 * data.loaded) / data.total)
          : 0;
      },
    }) as Promise<UploadResultType>
  )
    .then((res) => {
      // 所有excel导入的特殊处理
      if (res.hasError) {
        const failRecords = res.failRecords;
        const message = failRecords
          .map((item) => `第${item.lineNum}行：${item.msg}`)
          .join("。");

        handleError(message);
        return;
      }
      handleSuccess(res);
    })
    .catch((error: AxiosError<{ message: string }> | null) => {
      handleError(error?.response?.data?.message);
    });
};

function handleError(message: string | undefined) {
  canUpload.value = false;
  loading.value = false;
  result.value = {
    success: false,
    message: message || "上传错误",
  };
  emit("uploadError", result.value);
}
function handleSuccess(data: UploadResultType) {
  canUpload.value = false;
  loading.value = false;
  result.value = {
    success: true,
    message: "上传成功！",
  };
  emit("uploadSuccess", {
    ...result.value,
    data,
  });
}

function handleFormatError() {
  const content = `只支持文件格式为${props.format.join("/")}`;
  result.value = {
    success: false,
    message: content,
  };
  loading.value = false;
  emit("validError", result.value);
}
function handleExceededSize() {
  const content = `文件大小不能超过${Math.floor(props.maxSize / 1024)}M`;
  result.value = {
    success: false,
    message: content,
  };
  loading.value = false;
  emit("validError", result.value);
}
</script>
