<template>
  <div class="file-upload">
    <el-input
      v-model.trim="attachmentName"
      placeholder="文件名称"
      readonly
    ></el-input>
    <el-upload
      ref="uploadRef"
      :action="uploadUrl"
      :headers="headers"
      :limit="1"
      :data="uploadDataDict"
      :show-file-list="false"
      :auto-upload="autoUpload"
      :http-request="customRequest"
      :disabled="disabled"
      :before-upload="handleBeforeUpload"
      :on-change="handleFileChange"
      :on-error="handleError"
      :on-success="handleSuccess"
    >
      <template #trigger>
        <el-button type="primary" :disabled="loading">选择</el-button>
      </template>
      <el-button
        v-if="!autoUpload"
        type="primary"
        :loading="loading"
        :disabled="!canUpload"
        style="margin-left: 10px"
        @click.stop="startUpload"
        >开始上传</el-button
      >
    </el-upload>
  </div>
</template>

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

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

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

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

  const uploadRef = ref();
  const attachmentName = ref('');
  const canUpload = ref(false);
  const uploadDataDict = ref({});
  const headers = ref({ md5: '' });
  const result = ref({ success: true, message: '' });
  const loading = ref(false);

  function startUpload() {
    loading.value = true;
    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 (props.autoUpload || !uploadFiles.length) return;

    attachmentName.value = uploadFile.name || '';
    // Element Plus 的 status 有 ready, uploading, success, fail
    canUpload.value = uploadFile.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;
  }

  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() {
    canUpload.value = false;
    loading.value = false;
    result.value = {
      success: false,
      message: '上传失败',
    };
    emit('uploadError', result.value);
  }
  function handleSuccess(response: any, uploadFile: UploadFile) {
    canUpload.value = false;
    loading.value = false;
    result.value = {
      success: true,
      message: '上传成功！',
    };
    ElMessage.success('上传成功！');
    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)}M`;
    result.value = {
      success: false,
      message: content,
    };
    loading.value = false;
    ElMessage.error(content);
    emit('validError', result.value);
  }
  function setAttachmentName(name: string) {
    attachmentName.value = name;
  }

  defineExpose({ setAttachmentName });
</script>

<style lang="less">
  .file-upload {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;

    .arco-input-wrapper {
      flex-grow: 2;
      margin-right: 10px;
    }
    .arco-upload {
      flex-grow: 0;
      flex-shrink: 0;
    }
  }
</style>
