<script lang="ts" setup>
import { IconMore, IconSUpload } from '@computing/opendesign-icons';
import { PropType, reactive, Ref, ref, toRefs } from 'vue';
import { UploadFile } from 'element-plus/es/components/upload/src/upload';
import { IUploadConfig } from '@/model/base-interface';
import $http from '@/utils/http-service';

const props = defineProps({
  data: {
    type: Object as PropType<IUploadConfig>,
    required: true,
    default: () => {
      return {};
    },
  },
});

const { data } = toRefs(props);
const uploadConfig = reactive(data.value);
const style = { width: data.value.width ? data.value.width + 'px' : '100%' };

const fileItem: Ref<UploadFile | null> = ref(null);
const fileName = ref('');
const upload = ref();
const isError = ref(false);
const errorMsg = ref('');
const handleSuccess = (response: any, file: UploadFile, list: UploadFile[]) => {
  if (uploadConfig.onSuccess) {
    uploadConfig.onSuccess(response, file, list);
  }
};
const handleError = (error: any, file: UploadFile, list: UploadFile[]) => {
  isError.value = true;
  if (uploadConfig.onError) {
    uploadConfig.onError(error, file, list);
  }
};

const handleExceed = (files: UploadFile[]) => {
  upload.value.clearFiles();
  upload.value.handleStart(files[0]);
};

const handleChange = (file: UploadFile, list: UploadFile[]) => {
  if (file.status === 'ready' && uploadConfig.onAddItem) {
    fileItem.value = file;
    isError.value = false;
    errorMsg.value = '';
    fileName.value = file.name;
    isError.value = !uploadConfig.onAddItem(file, list, errorMsg, upload.value);
    if (isError.value) {
      fileName.value = '';
    } else {
      if (uploadConfig.onAddItemSuccess) {
        uploadConfig.onAddItemSuccess(file);
      }
    }
  }
};
const handlePreview = (file: UploadFile) => {
  if (uploadConfig.onPreview) {
    uploadConfig.onPreview(file);
  }
};
const handleRemove = (file: UploadFile, list: UploadFile[]) => {
  if (uploadConfig.onRemove) {
    uploadConfig.onRemove(file, list);
  }
};
const handleProgress = (event: any, file: UploadFile, list: UploadFile[]) => {
  if (uploadConfig.onProgress) {
    uploadConfig.onProgress(event, file, list);
  }
};
const handleBeforeUpload = (file: UploadFile) => {
  if (uploadConfig.onBeforeUpload) {
    const result = Boolean(uploadConfig.onBeforeUpload(file)) || false;
    return !result;
  }
  return true;
};
const handleBeforeRemove = (file: UploadFile, list: UploadFile[]) => {
  if (uploadConfig.onBeforeRemove) {
    uploadConfig.onBeforeRemove(file, list);
  }
};

const customUpload = (options: any): Promise<any> => {
  return new Promise((resolve, reject) => {
    let header = {
      headers: {},
    };
    header.headers = uploadConfig.headers;
    const url = uploadConfig.action as string;
    const param = options.formData;
    if (uploadConfig.isCustomUpload) {
      $http
        .post(url, param, header)
        .then(res => {
          resolve(res);
        })
        .catch(error => {
          reject(error);
        });
    } else {
      reject(new Error('failed'));
    }
  });
};
defineExpose({
  clear: () => {
    if (fileItem.value !== null) {
      upload.value.handleRemove(fileItem.value);
    }
    fileName.value = '';
  },
  setError: (msg: string) => {
    isError.value = true;
    errorMsg.value = msg;
  },
  clearError: () => {
    isError.value = false;
    errorMsg.value = '';
  },
  customUpload,
});
</script>

<template>
  <div class="file-upload" :style="uploadConfig.type !== 'button' ? style : {}">
    <el-upload
      :id="uploadConfig.id || 'fileUpload'"
      ref="upload"
      :disabled="uploadConfig.disabled"
      :action="uploadConfig.action"
      :name="uploadConfig.name"
      :limit="1"
      :headers="uploadConfig.headers"
      :data="uploadConfig.data"
      :on-exceed="handleExceed"
      :on-change="handleChange"
      :on-success="handleSuccess"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :on-error="handleError"
      :on-progress="handleProgress"
      :before-upload="handleBeforeUpload"
      :before-remove="handleBeforeRemove"
      :auto-upload="uploadConfig.isAutoUpload"
      list-type="text"
      :accept="uploadConfig.accept"
    >
      <template #trigger>
        <el-button
          v-if="uploadConfig.type === 'button'"
          class="upload-button"
          :class="{ disabled: uploadConfig.disabled }"
          :disabled="uploadConfig.disabled"
        >
          <el-icon>
            <IconSUpload />
          </el-icon>
          {{ $t('COMMON_UPLOAD') }}
        </el-button>
        <el-input
          v-else
          v-model="fileName"
          :placeholder="uploadConfig.placeholder || $t('ADD_FILE')"
          :readonly="true"
          :class="{ 'error-input': isError && !uploadConfig.isNeedClearError }"
          :disabled="uploadConfig.disabled"
        >
          <template #suffix>
            <el-icon class="el-input__icon">
              <IconMore />
            </el-icon>
          </template>
        </el-input>
      </template>

      <span></span>
    </el-upload>
    <span v-if="isError && errorMsg" :id="uploadConfig.errorMsgId" class="error-text">
      {{ errorMsg }}
    </span>
  </div>
</template>

<style lang="scss" scoped>
.file-upload {
  display: inline-block;
  .error-text {
    display: inline-block;
    color: var(--o-color-danger);
    margin-top: 4px;
    line-height: 16px;
  }

  .upload-button {
    border: none;
    color: var(--o-text-color-secondary);
    background-color: var(--o-bg-color-base);
  }

  .upload-button:hover {
    color: var(--o-button-color_hover);
  }
  .disabled,
  .disabled:hover {
    color: var(--o-button-color_disabled) !important;
  }

  :deep(.el-input) {
    .el-input__suffix .el-input__icon {
      margin-right: 0;
    }

    .el-input__inner {
      padding-top: 8px;
      padding-bottom: 8px;
    }

    &:hover {
      .el-input__suffix .el-input__icon {
        color: var(--o-input-border-color_hover);
      }
    }
  }
}
</style>
<style lang="scss">
.file-upload {
  .el-upload {
    display: flex;
  }
  .el-upload-list {
    display: none;
  }

  .error-input {
    .el-input__inner {
      background: var(--o-color-danger-secondary);
      border-color: var(--o-form-item-border-color_error);
      box-shadow: 0 0 0 1px var(--el-color-danger) inset;
    }
    .el-input__inner:focus {
      border-color: var(--o-form-item-border-color_error);
    }
  }
}
</style>
