<script setup>
import API from '@/api/files'
import { getMD5Async } from '@/utils/utils'
import { getToken } from '@/utils/login-helper'
import { Upload } from 'tus-js-client'
import asyncPool from 'tiny-async-pool'
import { Message } from 'element-ui'

const props = defineProps({
  fileList: Array,
  onChange: Function,
  onRemove: Function,
  scene: {
    type: String,
    required: true
  },
  // 自动上传
  autoUpload: {
    type: Boolean,
    default: true
  },
  triggerUpload: {
    type: Number,
    default: 0
  },
  // 上传并发数
  concurrency: {
    type: Number,
    default: 4
  },
})
const vm = getCurrentInstance().proxy
const $attrs = vm.$attrs
const emit = defineEmits(['getFileId', 'getFileList', 'getErrorList', 'getPercent'])

let localFileList = ref([])
// 监听fileList，避免数据不一致
watch(
  () => props.fileList,
  (val) => localFileList.value = val,
  { immediate: true }
)
// 上传的进度
const progress = ref(0)

let timer = null
// 文件改变
function handleFileChange(filetype, fileList) {
  // 校验每一个文件是否满足上传条件
  const bool = props.onChange(filetype, fileList)
  if (!bool) {
    const idx = fileList.findIndex(v => v.uid === filetype.uid)
    fileList.splice(idx, 1)
  }
  timer && window.clearTimeout(timer)
  timer = setTimeout(() => {
    localFileList.value = fileList
    props.autoUpload && startUpload(fileList)
  }, 10)
}

watch(() => props.triggerUpload, startUpload)

// 每个filetype的回调
function iteratorFn(filetype, fileList) {
  return new Promise((resolve, reject) => {
    getMD5Async(filetype).then(md5 => {
      const fileData = {
        scene: props.scene,
        name: filetype.name,
        size: filetype.size,
        md5,
      }
      API.filesFast(fileData)
        .then(res => {
          //状态200 使用文件秒传接口 状态702使用文件上传接口或续点上传
          if (res.status === 200) {
            // 只有一个文件的情况
            if (fileList.length === 1) {
              let timer = setInterval(() => {
                if (progress.value > 98) {
                  progress.value = 99
                } else {
                  progress.value++
                }
              }, 80)
              progress.value = 100
              clearInterval(timer)
            }
            filetype.id = res.data.id
            resolve(filetype)
          } else if (res.status === 702) {
            // 文件小于100MB 正常上传流程 大于100MB 续点上传
            if (filetype.size < 1024 * 1024 * 100) {
              sendStorage(filetype, fileList).then(resolve).catch(reject)
            } else {
              sendStorage100(filetype, md5).then(resolve).catch(reject)
            }
          } else {
            resolve({ isError: true, filetype })
          }
        })
        .catch(error => {
          resolve({ isError: true, filetype })
        })
    })
  })
}

// 开始异步任务-【4个并发上传】
// 支持自定义上传文件
async function startUpload(customFileList = null) {
  // 支持自定义文件
  const getFileList = () =>  customFileList || localFileList.value
  // 需要上传的文件列表
  const fileList = getFileList().filter(v => !v.id && v.status === 'ready')
  if (fileList.length === 0) {
    return getFileList()
  }
  progress.value = 0
  const errorList = [] // 上传失败的任务
  const resList = [] // 上传成功的任务
  try {
    for await (const value of asyncPool(props.concurrency, fileList, iteratorFn)) {
      if (value.isError) {
        errorList.push(value.filetype)
      } else {
        // 每上传一个文件加一
        if (fileList.length > 1) {
          progress.value++
        }
        resList.push(value)
      }
    }
  } catch (error) {
    console.log(error)
    debugger
  }
  // 上传成功回调，分一个文件还是多个文件
  if (getFileList().length === 1) {
    emit('getFileId', getFileList()[0].id)
    emit('getSuccessList', getFileList())
    return getFileList()
  } else if (getFileList().length > 1) {
    emit('getFileId', getFileList().map(v => v.id).filter(v => v?.id))
    emit('getSuccessList', getFileList())
    return getFileList()
  }
  // 上传失败回调 errorList
  if (errorList.length > 0) {
    emit('getErrorList', errorList)
    return errorList
  }
}
vm.startUpload = startUpload

// 文件用表单方式上传
function sendStorage(filetype, fileList) {
  return new Promise((resolve, reject) => {
    let timer = null
    if (fileList.length === 1) {
      timer = setInterval(() => {
        if (progress.value > 98) {
          progress.value = 99
        } else {
          progress.value++
        }
      }, 100)
    }
    const formData = new FormData()
    formData.append('name', filetype.name)
    formData.append('file', filetype.raw)
    formData.append('scene', props.scene)
    API.filesUpload(formData)
      .then(res => {
        if (res.status === 200) {
          timer && clearInterval(timer)
          filetype.id = res.data.id
          resolve(filetype)
          progress.value = 100
        } else {
          resolve({ isError: true, filetype })
        }
      })
      .catch(error => {
        resolve({ isError: true, filetype })
      })
  })
}

let uploadList = []
// 大文件分片上传
function sendStorage100(filetype, md5) {
  let upload = null
  return new Promise((resolve, reject) => {
    // Create a new tus upload
    upload = new Upload(filetype.raw, {
      endpoint: location.origin + '/storage/api/v1/tus/upload/',
      headers: {
        'x-auth-token': getToken()
      },
      retryDelays: [0, 3000, 5000, 10000, 20000],
      metadata: {
        filename: filetype.name,
        filetype: filetype.type
      },
      onError: function(error) {
        console.log('Failed because: ' + error)
        resolve({ isError: true, filetype })
      },
      onProgress: function(bytesUploaded, bytesTotal) {
        progress.value = parseInt((bytesUploaded / bytesTotal) * 100)
        if (progress.value > 99) {
          progress.value = 99
        }
        // console.log('上传中', bytesUploaded, bytesTotal, upload.url)
      },
      onSuccess: function() {
        // console.log('上传成功', 'Download %s from %s', upload.file.name, upload.url)
        const data = {
          md5: md5,
          name: filetype.name,
          scene: props.scene,
          uploadUrl: upload.url
        }
        API.getStorageTusComplete(data)
          .then(res => {
            if (res.status === 200) {
              filetype.id = res.data.id
              resolve(filetype)
              progress.value = 100
            } else {
              resolve({ isError: true, filetype })
            }
          })
          .catch(error => {
            resolve({ isError: true, filetype })
          })
      },
      chunkSize: 1024 * 1024 * 50
    })
    // Check if there are any previous uploads to continue.
    upload.findPreviousUploads().then(function(previousUploads) {
      // Found previous uploads so we select the first one.
      if (previousUploads.length) {
        upload.resumeFromPreviousUpload(previousUploads[0])
      }
      // Start the upload
      upload.start()
    })
    uploadList.push(upload)
  })
}

// 回调上传进度
watch(progress, (val) => emit('getPercent', val))

// 文件移除
function handleFileRemove(filetype, fileList) {
  uploadList.forEach(upload => {
    upload?.abort(true)
  })
  uploadList.length = 0
  props.onRemove && props.onRemove(filetype, fileList)
}

// 文件数量超出最大值给出提示
function handleExceed() {
  Message.warning(`您当前选择的文件过多，最多不超过${$attrs.limit}个文件`)
}

</script>

<template>
  <el-upload
    :action="''"
    :on-change="handleFileChange"
    :on-remove="handleFileRemove"
    :on-exceed="handleExceed"
    :auto-upload="props.autoUpload"
    :file-list="localFileList"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <slot></slot>
  </el-upload>
</template>

<style scoped></style>
