<template>
  <el-dialog
    v-model="visible"
    :width="422"
    :title="title"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    @open="modalBeforeOpen"
  >
    <slot></slot>
    <div class="import-box">
      <div class="import-temp">
        <span>模板下载：</span>
        <div class="temp-btn">
          <a v-if="downloadUrl" :href="downloadUrl" :download="dfilename">{{
            dfilename
          }}</a>
          <el-button
            v-else-if="downloadHandle"
            type="primary"
            link
            @click="downloadHandle"
            >{{ dfilename }}</el-button
          >
        </div>
      </div>
      <div class="import-body">
        <el-upload
          v-if="visible"
          ref="uploadRef"
          drag
          :action="uploadUrl"
          :headers="headers"
          :accept="accept"
          :data="uploadDataDict"
          :show-file-list="true"
          :auto-upload="autoUpload"
          :http-request="customRequest"
          :disabled="disabled"
          :before-upload="handleBeforeUpload"
          :file-list="customFileList"
          :on-change="handleFileChange"
          :on-error="handleError"
          :on-success="handleSuccess"
          :limit="1"
        >
          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
          <div class="el-upload__text">
            将文件拖到此处，或<em>点击上传</em>
          </div>
        </el-upload>
        <p
          v-if="result.message && !result.success"
          class="tips-info tips-error"
        >
          {{ result.message }}
        </p>
      </div>
    </div>

    <template v-if="!autoUpload" #footer>
      <el-button @click="close">取消</el-button>
      <el-button
        type="primary"
        :disabled="loading || !canUpload"
        @click="confirm"
        >确认</el-button
      >
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
  import { computed, ref } from 'vue';
  import { fileMD5 } from '@/utils/md5';
  import { ElMessage, ElMessageBox } from 'element-plus';
  import type {
    UploadProps,
    UploadRequestOptions,
    UploadFile,
    UploadFiles,
    UploadProgressEvent,
  } from 'element-plus';
  import { UploadFilled } from '@element-plus/icons-vue';
  import axios, { AxiosError, AxiosHeaders } from 'axios';

  import useModal from '@/hooks/modal';

  defineOptions({
    name: 'ImportDialog',
  });

  /* modal */
  const { visible, open, close } = useModal();
  defineExpose({ open, close });

  interface PromiseFunc {
    (): Promise<void>;
  }

  const props = withDefaults(
    defineProps<{
      title: string;
      uploadUrl: string;
      format?: string[];
      uploadData?: Record<string, any>;
      maxSize?: number;
      uploadFilename?: string;
      autoUpload?: boolean;
      disabled?: boolean;
      uploadFileAlias?: string;
      downloadUrl?: string;
      downloadFilename?: string;
      downloadHandle?: PromiseFunc;
      beforeSubmitHandle?: PromiseFunc;
      successMessage?: string;
    }>(),
    {
      title: '文件上传',
      uploadUrl: '',
      format: () => ['xls', 'xlsx'],
      uploadData: () => {
        return {};
      },
      maxSize: 20 * 1024 * 1024,
      uploadFileAlias: 'file',
      autoUpload: true,
      disabled: false,
      successMessage: '上传成功',
    }
  );

  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 customFileList = ref<UploadFile[]>([]);

  const dfilename = computed(() => {
    if (props.downloadFilename) return props.downloadFilename;
    return props.downloadUrl ? props.downloadUrl.split('/').pop() : '';
  });
  const accept = computed(() => {
    return props.format.map((el) => `.${el}`).join();
  });

  async function confirm() {
    loading.value = true;
    if (props.beforeSubmitHandle) {
      let handleResult = true;
      await props.beforeSubmitHandle().catch(() => {
        handleResult = false;
      });
      if (!handleResult) return;
    }
    uploadRef.value?.submit();
  }

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

  function handleFileChange(uploadFile: UploadFile, uploadFiles: UploadFiles) {
    if (uploadFiles.length) {
      // Element Plus 的 onChange 会在文件状态改变时都触发，包括移除
      // 这里只处理添加新文件的场景，移除由 el-upload 内部处理
      if (uploadFile.status === 'ready') {
        customFileList.value = [uploadFile];
      } else if (
        uploadFile.status === 'success' ||
        uploadFile.status === 'fail'
      ) {
        // 如果是上传成功或失败，也更新列表以显示状态
        const index = customFileList.value.findIndex(
          (f) => f.uid === uploadFile.uid
        );
        if (index !== -1) {
          customFileList.value.splice(index, 1, uploadFile);
        } else {
          customFileList.value = [uploadFile]; // 如果之前列表为空
        }
      }
      // 清理旧的 result message
      const currentFileInList = customFileList.value.find(
        (f) => f.uid === uploadFile.uid
      );
      if (currentFileInList && currentFileInList.uid !== uploadFile.uid) {
        // 理论上这里 customFileList 只有一个文件
        result.value = {
          success: true,
          message: '',
        };
      }
    } else {
      customFileList.value = [];
      result.value = {
        success: true,
        message: '',
      };
    }
    canUpload.value = customFileList.value[0]?.status === 'ready';
  }

  async function handleBeforeUpload(file: File): Promise<boolean | 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 fileMD5(file);
    headers.value.md5 = md5;

    if (props.autoUpload) loading.value = true;

    return true;
  }

  interface UploadResultType {
    hasError: boolean;
    failRecords: Array<{
      msg: string;
      lineNum: number;
    }>;
    // Add other properties if your backend returns more fields on success
    [key: string]: any;
  }

  function customRequest(options: UploadRequestOptions) {
    // Changed return type to any
    const { onProgress, file, data } = options;

    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');

    return axios.post(options.action, formData, {
      headers: options.headers as AxiosHeaders,
      onUploadProgress: ({ loaded, total }) => {
        onProgress({
          percent: total ? Math.floor((100 * loaded) / total) : 0,
        } as UploadProgressEvent);
      },
    });
  }

  function handleError(
    error: Error,
    uploadFile: UploadFile,
    uploadFiles: UploadFiles
  ) {
    canUpload.value = false;
    loading.value = false;
    result.value = {
      success: false,
      message: error.message || '上传错误',
    };
    // Update the file in customFileList to reflect the error status
    const fileInList = customFileList.value.find(
      (f) => f.uid === uploadFile.uid
    );
    if (fileInList) {
      fileInList.status = 'fail';
    }
    emit('uploadError', result.value);
  }
  function handleSuccess(response: any, uploadFile: UploadFile) {
    canUpload.value = false;
    loading.value = false;
    result.value = {
      success: true,
      message: '上传成功！',
    };
    ElMessage.success(props.successMessage);
    emit('uploadSuccess', {
      ...result.value,
      filename: uploadFile.name,
      response,
    });
  }

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

  function modalBeforeOpen() {
    canUpload.value = false;
    result.value = {
      success: true,
      message: '',
    };
    headers.value = { md5: '' };
    loading.value = false;
    uploadDataDict.value = {};
    customFileList.value = [];
  }
</script>

<style lang="less">
  .import-box {
    .import-temp {
      display: flex;
      justify-content: space-between;
      margin-bottom: 10px;

      > span {
        flex-grow: 0;
        flex-shrink: 0;
        height: 20px;
        line-height: 20px;
        display: block;
      }

      .temp-btn {
        flex-grow: 2;
        text-align: left;

        > a {
          flex-grow: 2;
          line-height: 20px;
          color: var(--color-primary);

          &:hover {
            text-decoration: underline;
            opacity: 0.8;
          }
        }
      }
      .arco-btn {
        line-height: 20px;
        height: auto;
        padding: 0;
        background: transparent;
        border: none;

        &:hover {
          text-decoration: underline;
          opacity: 0.8;
        }
      }
    }
    .arco-upload-drag {
      padding: 40px 0;
      > div:first-child {
        height: 54px;
        background-image: url(assets/images/upload-icon.png);
        background-size: auto 100%;
        background-repeat: no-repeat;
        background-position: center;
        margin-bottom: 16px;
      }
      svg {
        display: none;
      }
    }

    .arco-upload-list-item {
      margin-top: 8px !important;
      background-color: var(--color-fill-1);
      border-radius: var(--border-radius-small);
      .arco-upload-list-item-operation {
        margin: 0 12px;
      }

      .svg-icon {
        vertical-align: -2px;
      }
      .arco-upload-list-item-file-icon {
        margin-right: 6px;
        color: inherit;
      }

      &.arco-upload-list-item-error {
        .arco-upload-list-item-file-icon {
          color: var(--color-danger);
        }
      }
    }
    .arco-upload-progress {
      > * {
        display: none;
      }
      .arco-upload-icon-success {
        display: block;
      }
    }

    .tips-info {
      max-height: 100px;
      overflow: hidden;
      margin-top: 5px;
    }
  }
</style>
