<script lang="ts" setup>
import { IconMore, IconSUpload, IconXSolid } 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 container = ref<HTMLElement>();
const isError = ref(false);
const errorMsg = ref('');
const isDragOver = ref(false);

const addFile = (file?: File | null) => {
  if (!file || uploadConfig.disabled) {
    return;
  }
  upload.value.clearFiles();
  upload.value.handleStart(file);
};

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 handleMouseEnter = () => {
  if (uploadConfig.enablePaste && !uploadConfig.disabled) {
    container.value?.focus({ preventScroll: true });
  }
};

const handleDragEnter = () => {
  if (uploadConfig.enableDrop && !uploadConfig.disabled) {
    isDragOver.value = true;
  }
};

const handleDragLeave = () => {
  isDragOver.value = false;
};

const handleDrop = (event: DragEvent) => {
  isDragOver.value = false;
  if (!uploadConfig.enableDrop) {
    return;
  }
  addFile(event.dataTransfer?.files?.[0]);
};

const handlePaste = (event: ClipboardEvent) => {
  if (!uploadConfig.enablePaste) {
    return;
  }
  addFile(event.clipboardData?.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 clearSelectedFile = () => {
  if (fileItem.value !== null) {
    upload.value.handleRemove(fileItem.value);
  }
  fileItem.value = null;
  fileName.value = '';
  isError.value = false;
  errorMsg.value = '';
};

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

function trigger(): void {
  const input = upload.value.$el.querySelector('input[type=file]');
  input.value = '';
  input.click();
}

defineExpose({
  clear: () => {
    clearSelectedFile();
  },
  setError: (msg: string) => {
    isError.value = true;
    errorMsg.value = msg;
  },
  clearError: () => {
    isError.value = false;
    errorMsg.value = '';
  },
  customUpload
});
</script>

<template>
  <div
    ref="container"
    class="file-upload"
    :class="{ 'drag-over': isDragOver }"
    :style="uploadConfig.type !== 'button' ? style : {}"
    :tabindex="uploadConfig.enablePaste ? 0 : undefined"
    @mouseenter="handleMouseEnter"
    @dragenter.prevent="handleDragEnter"
    @dragover.prevent="handleDragEnter"
    @dragleave.prevent="handleDragLeave"
    @drop.prevent="handleDrop"
    @paste="handlePaste"
  >
    <el-input
      v-if="uploadConfig.type !== 'button'"
      v-model="fileName"
      class="upload-display-input"
      :placeholder="
        uploadConfig.placeholder ||
        (uploadConfig.enableDrop || uploadConfig.enablePaste
          ? $t('ADD_FILE')
          : $t('ADD_FILE_CLICK'))
      "
      :readonly="true"
      :class="{ 'error-input': isError && !uploadConfig.isNeedClearError }"
      :disabled="uploadConfig.disabled"
      @click="trigger"
    />
    <el-button
      v-if="fileName && uploadConfig.type !== 'button' && !uploadConfig.disabled"
      class="upload-clear"
      text
      @click.stop="clearSelectedFile"
    >
      <el-icon>
        <IconXSolid />
      </el-icon>
    </el-button>
    <el-upload
      :id="uploadConfig.id || 'fileUpload'"
      ref="upload"
      :class="{ 'input-upload-selector': uploadConfig.type !== 'button' }"
      :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-button v-else class="upload-more-button" :disabled="uploadConfig.disabled">
          <el-icon>
            <IconMore />
          </el-icon>
        </el-button>
      </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 {
  position: relative;
  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);
  }
  &.drag-over {
    :deep(.upload-display-input .el-input__inner) {
      border-color: var(--o-input-border-color_hover);
      box-shadow: 0 0 0 1px var(--o-input-border-color_hover) inset;
    }
  }
  .disabled,
  .disabled:hover {
    color: var(--o-button-color_disabled) !important;
  }

  :deep(.el-input) {
    .el-input__inner {
      padding-top: 8px;
      padding-bottom: 8px;
      padding-right: 56px;
    }
  }

  .upload-display-input {
    width: 100%;
  }

  .input-upload-selector {
    position: absolute;
    top: 0;
    right: 0;
    width: 32px;
    height: 32px;
    z-index: 2;
  }

  .upload-more-button {
    min-width: 32px;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    color: var(--o-text-color-secondary);
    background: transparent;
  }

  .upload-more-button:hover {
    color: var(--o-input-border-color_hover);
    background: transparent;
  }

  .upload-clear {
    position: absolute;
    top: -7px;
    right: -7px;
    z-index: 3;
    min-width: 16px;
    width: 16px;
    height: 16px;
    padding: 0;
    border: 1px solid var(--o-border-color-base);
    border-radius: 50%;
    color: var(--o-text-color-secondary);
    background: var(--o-bg-color-base);
  }

  .upload-clear:hover {
    color: var(--o-color-danger);
  }
}
</style>
<style lang="scss">
.file-upload {
  .el-upload {
    display: flex;
  }
  .input-upload-selector {
    .el-upload {
      width: 100%;
      height: 100%;
    }
  }
  .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>