<script>
import UploadList from './upload-list';
import Upload from './upload';
import ElProgress from 'element-ui/packages/progress';
import Migrating from 'element-ui/src/mixins/migrating';
import emitter from 'element-ui/src/mixins/emitter';

function noop() {}
const type = data => Object.prototype.toString.call(data).slice(8, -1).toLowerCase()
export default {
  name: 'ElUpload',

  mixins: [emitter, Migrating],

  components: {
    ElProgress,
    UploadList,
    Upload
  },

  provide() {
    return {
      uploader: this
    };
  },

  inject: {
      elForm: {
          default: ''
      },
      elFormItem: {
          default: ''
      }
  },

  props: {
      value: {
          type: Array,
          default: () => []
      },
    action: {
      type: String,
        default() {
          return this.$ELEMENT.upload?.action || '#'
        }
    },
    headers: {
      type: Object,
      default() {
        return this.$ELEMENT.upload?.headers || {};
      }
    },
    data: {
        type: Object,
        default() {
            return this.$ELEMENT.upload?.data;
        }
    },
    multiple: Boolean,
    name: {
      type: String,
        default() {
            return this.$ELEMENT.upload?.name || 'file'
        }
    },
    drag: {
        type: Boolean,
        default() {
            return this.$ELEMENT.upload?.drag;
        }
    },
    dragger: Boolean,
    withCredentials: Boolean,
    showFileList: {
      type: Boolean,
      default: true
    },
    accept: String,
    type: {
      type: String,
      default: 'select'
    },
    beforeUpload: {
        type: Function,
        default() {
            return this.$ELEMENT.upload?.beforeUpload?.call(this, ...arguments) || noop()
        }
    },
    beforeRemove: {
        type: Function,
        default() {
            return this.$ELEMENT.upload?.beforeRemove?.call(this, ...arguments)
        }
    },
    onRemove: {
        type: Function,
        default() {
            return this.$ELEMENT.upload?.onRemove?.call(this, ...arguments) || noop()
        }
    },
    onChange: {
      type: Function,
        default() {
            return this.$ELEMENT.upload?.onChange?.call(this, ...arguments) || noop()
        }
    },
    onPreview: Function,
    onSuccess: {
      type: Function,
        default() {
            return this.$ELEMENT.upload?.onSuccess?.call(this, ...arguments) || noop()
        }
    },
    onProgress: {
      type: Function,
        default() {
            return this.$ELEMENT.upload?.onProgress?.call(this, ...arguments) || noop()
        }
    },
    onError: {
      type: Function,
        default() {
            return this.$ELEMENT.upload?.onError?.call(this, ...arguments) || noop()
        }
    },
      fileList: {
      type: Array,
      default() {
        return [];
      }
    },
    autoUpload: {
      type: Boolean,
      default() {
          if (this.$ELEMENT.hasOwnProperty('upload') && this.$ELEMENT.upload.hasOwnProperty('autoUpload')) {
              return this.$ELEMENT.upload?.autoUpload
          }
          return true
      }
    },
    listType: {
      type: String,
      default: 'text' // text,picture,picture-card
    },
    httpRequest: {
        type: Function,
        default(props) {
            return this.$ELEMENT.upload?.httpRequest?.call(this, props) || noop(props)
        }
    },
    disabled: Boolean,
    limit: Number,
    onExceed: {
      type: Function,
      default() {
        return this.$ELEMENT.upload?.onExceed?.call(this, ...arguments) || noop(...arguments)
      }
    }
  },

  data() {
    return {
      uploadFiles: [],
      dragOver: false,
      draging: false,
      tempIndex: 1
    };
  },

  computed: {
    uploadDisabled() {
      return this.disabled || (this.elForm || {}).disabled;
    },
      handlePreview() {
        return this.onPreview || this.$ELEMENT.upload?.onPreview
      }
  },

  watch: {
    listType(type) {
      if (type === 'picture-card' || type === 'picture') {
        this.uploadFiles = this.uploadFiles.map(file => {
          if (!file.url && file.raw) {
            try {
              file.url = URL.createObjectURL(file.raw);
            } catch (err) {
              console.error('[Element Error][Upload]', err);
            }
          }
          return file;
        });
      }
    },
    // fileList: {
    //   immediate: true,
    //   handler(fileList) {
    //       if (fileList && fileList.length) {
    //           this.uploadFiles = fileList.map(item => {
    //               item.uid = item.uid || (Date.now() + this.tempIndex++);
    //               item.status = item.status || 'success';
    //               return item;
    //           });
    //       }
    //   }
    // },
      value: {
          immediate: true,
          handler(fileList) {
              this.uploadFiles = fileList.map(item => {
                  item.uid = item.uid || (Date.now() + this.tempIndex++);
                  item.status = item.status || 'success';
                  return item;
              });
          }
      }
  },
  methods: {
    handleStart(rawFile) {
      rawFile.uid = Date.now() + this.tempIndex++;
      let file = {
        status: 'ready',
        name: rawFile.name,
        size: rawFile.size,
        percentage: 0,
        uid: rawFile.uid,
        raw: rawFile
      };

      if (this.listType === 'picture-card' || this.listType === 'picture') {
        try {
          file.url = URL.createObjectURL(rawFile);
        } catch (err) {
          console.error('[Element Error][Upload]', err);
          return;
        }
      }

      this.uploadFiles.push(file);

      // let files = this.onChange(file, this.uploadFiles);
      // this.getResult(files, this.uploadFiles)
    },
    handleProgress(ev, rawFile) {
      const file = this.getFile(rawFile);
      this.onProgress(ev, file, this.uploadFiles);
      file.status = 'uploading';
      file.percentage = ev.percent || 0;
    },
    handleSuccess(res, rawFile) {
        const file = this.getFile(rawFile);
      if (file) {
        file.status = 'success';
        file.response = res;

        let files = this.onSuccess(res, file, this.uploadFiles);
        if (files) {
            this.$emit('input', [...files])
            this.dispatch('ElFormItem', 'el.form.change', files);
        }
      }
    },
    handleError(err, rawFile) {
      const file = this.getFile(rawFile);
      const fileList = this.uploadFiles;

      file.status = 'fail';

      fileList.splice(fileList.indexOf(file), 1);

      this.onError(err, file, this.uploadFiles);
      // let files = this.onChange(file, this.uploadFiles);
      // this.getResult(files, this.uploadFiles)
    },
    handleRemove(file, raw) {
      if (raw) {
        file = this.getFile(raw);
      }

      let doRemove = () => {
        this.abort(file);
        let fileList = this.uploadFiles;
        fileList.splice(fileList.indexOf(file), 1);
        this.$emit('input', fileList)
          this.dispatch('ElFormItem', 'el.form.change', fileList);
        // 非自动上传时删除文件
        //   this.getResult(this.uploadFiles)

        this.onRemove(file, fileList);
      };

      if (!this.beforeRemove) {
        doRemove();
      } else if (typeof this.beforeRemove === 'function') {
        const before = this.beforeRemove(file, this.uploadFiles);
        if (before && before.then) {
          before.then(() => {
            doRemove();
          }, noop);
        } else if (before !== false) {
          doRemove();
        }
      }
    },
    getFile(rawFile) {
      let fileList = this.uploadFiles;
      let target;
      fileList.every(item => {
        target = rawFile.uid === item.uid ? item : null;
        return !target;
      });
      return target;
    },
    abort(file) {
      this.$refs['upload-inner'].abort(file);
    },
    clearFiles() {
      this.uploadFiles = [];
    },
    submit() {
      this.uploadFiles
        .filter(file => file.status === 'ready')
        .forEach(file => {
          this.$refs['upload-inner'].upload(file.raw);
        });
    },
    getMigratingConfig() {
      return {
        props: {
          'default-file-list': 'default-file-list is renamed to file-list.',
          'show-upload-list': 'show-upload-list is renamed to show-file-list.',
          'thumbnail-mode': 'thumbnail-mode has been deprecated, you can implement the same effect according to this case: http://element.eleme.io/#/zh-CN/component/upload#yong-hu-tou-xiang-shang-chuan'
        }
      };
    },
      getResult(files, fileList) {
        let values = []
          if (files && files.length) {
              values = files
          } else {
              values = fileList
          }
          this.$emit('input', values)

      },
      onBeforeUpload(file) {
          let names = file.name.split('.') || []
          let type = names[names.length - 1]
          if (this.accept) {
              let types = this.accept.split(',')
              if (!types.includes(type)) {
                  this.$message?.warning(`请上传${this.accept}格式`)
                  return false
              }
          }
        return this.beforeUpload && this.beforeUpload(file)
      }
  },

  beforeDestroy() {
    this.uploadFiles.forEach(file => {
      if (file.url && file.url.indexOf('blob:') === 0) {
        URL.revokeObjectURL(file.url);
      }
    });
  },

  render(h) {
    let uploadList;
    if (this.showFileList) {
      uploadList = (
        <UploadList
          disabled={this.uploadDisabled}
          listType={this.listType}
          files={this.uploadFiles}
          limit={this.limit}
          on-remove={this.handleRemove}
          handlePreview={this.handlePreview}>
          {
            (props) => {
              if (this.$scopedSlots.file) {
                return this.$scopedSlots.file({
                  file: props.file
                });
              }
            }
          }
        </UploadList>
      );
    }

    const uploadData = {
      props: {
        type: this.type,
        drag: this.drag,
        action: this.action,
        multiple: this.multiple,
        'before-upload': this.onBeforeUpload,
        'with-credentials': this.withCredentials,
        headers: this.headers,
        name: this.name,
        data: this.data,
        accept: this.accept,
        fileList: this.uploadFiles,
        autoUpload: this.autoUpload,
        listType: this.listType,
        disabled: this.uploadDisabled,
        limit: this.limit,
        'on-exceed': this.onExceed,
        'on-start': this.handleStart,
        'on-progress': this.handleProgress,
        'on-success': this.handleSuccess,
        'on-error': this.handleError,
        'on-preview': this.handlePreview,
        'on-remove': this.handleRemove,
        'http-request': this.httpRequest
      },
      ref: 'upload-inner'
    };

    let file = this.uploadFiles[this.uploadFiles.length - 1]
    let defaultSlot = (
        file != undefined ? <img src={file.fileUrl || file.url} class="avatar-preview" /> : <i class="el-icon-plus avatar-uploader-icon"></i>
    );

    const trigger = this.$slots.trigger || this.$slots.default;
    let isLimit = this.uploadFiles.length < this.limit;
    const uploadComponent = <upload {...uploadData}>{trigger}</upload>;
    const avatarComponent = <upload {...uploadData}>{defaultSlot}</upload>;
    return (
      <div>
        { this.listType === 'picture-card' ? uploadList : ''}
        {
          this.$slots.trigger
            ? [uploadComponent, this.$slots.default]
            : (this.listType === 'picture-card' && !this.showFileList) ? avatarComponent : uploadComponent
        }
        {this.$slots.tip}
        { this.listType !== 'picture-card' ? uploadList : ''}
      </div>
    );
  }
};
</script>
