import { ref, watch } from 'vue'
import { NOOP } from '@vue/shared'
import { cloneDeep } from 'lodash-unified'

// Inline types
import type {
  ListType,
  UploadFile,
  UploadStatus,
  ElFile,
  ElUploadProgressEvent,
  IUseHandlersProps,
} from './upload.type'
type UploadRef = {
  abort: (file: UploadFile) => void
  upload: (file: ElFile) => void
}
// helpers
function getFile(rawFile: ElFile, uploadFiles: UploadFile[]) {
  return uploadFiles.find((file) => file.uid === rawFile.uid)
}

function genUid(seed: number) {
  return Date.now() + seed
}

export default (props: IUseHandlersProps) => {
  const uploadFiles = ref<UploadFile[]>([])
  const uploadRef = ref<UploadRef>(null)

  let tempIndex = 1

  function abort(file: UploadFile) {
    uploadRef.value.abort(file)
  }

  function clearFiles(
    status: UploadStatus[] = ['ready', 'uploading', 'success', 'fail']
  ) {
    uploadFiles.value = uploadFiles.value.filter((row) => {
      return !status.includes(row.status)
    })
  }

  function handleError(err: Error, rawFile: ElFile) {
    const file = getFile(rawFile, uploadFiles.value)
    file.status = 'fail'
    uploadFiles.value.splice(uploadFiles.value.indexOf(file), 1)
    props.onError(err, file, uploadFiles.value)
    props.onChange(file, uploadFiles.value)
  }

  function handleProgress(ev: ElUploadProgressEvent, rawFile: ElFile) {
    const file = getFile(rawFile, uploadFiles.value)
    props.onProgress(ev, file, uploadFiles.value)
    file.status = 'uploading'
    file.percentage = ev.percent || 0
  }

  function handleSuccess(res: any, rawFile: ElFile) {
    const file = getFile(rawFile, uploadFiles.value)
    if (file) {
      file.status = 'success'
      file.response = res
      props.onSuccess(res, file, uploadFiles.value)
      props.onChange(file, uploadFiles.value)
    }
  }

  function handleStart(rawFile: ElFile) {
    const uid = genUid(tempIndex++)
    rawFile.uid = uid
    const file: UploadFile = {
      name: rawFile.name,
      percentage: 0,
      status: 'ready',
      size: rawFile.size,
      raw: rawFile,
      uid,
    }
    if (props.listType === 'picture-card' || props.listType === 'picture') {
      try {
        file.url = URL.createObjectURL(rawFile)
      } catch (err) {
        console.error('[Element Error][Upload]', err)
        props.onError(err, file, uploadFiles.value)
      }
    }
    uploadFiles.value.push(file)
    props.onChange(file, uploadFiles.value)
  }

  function handleRemove(file: UploadFile, raw: ElFile) {
    if (raw) {
      file = getFile(raw, uploadFiles.value)
    }
    const revokeObjectURL = () => {
      if (file.url && file.url.indexOf('blob:') === 0) {
        URL.revokeObjectURL(file.url)
      }
    }
    const doRemove = () => {
      abort(file)
      const fileList = uploadFiles.value
      fileList.splice(fileList.indexOf(file), 1)
      props.onRemove(file, fileList)
      revokeObjectURL()
    }
    if (!props.beforeRemove) {
      doRemove()
    } else if (typeof props.beforeRemove === 'function') {
      const before = props.beforeRemove(file, uploadFiles.value)
      if (before instanceof Promise) {
        before
          .then(() => {
            doRemove()
          })
          .catch(NOOP)
      } else if (before !== false) {
        doRemove()
      }
    }
  }

  function submit() {
    uploadFiles.value
      .filter((file) => file.status === 'ready')
      .forEach((file) => {
        uploadRef.value.upload(file.raw)
      })
  }

  watch(
    () => props.listType,
    (val: ListType) => {
      if (val === 'picture-card' || val === 'picture') {
        uploadFiles.value = uploadFiles.value.map((file) => {
          if (!file.url && file.raw) {
            try {
              file.url = URL.createObjectURL(file.raw)
            } catch (err) {
              props.onError(err, file, uploadFiles.value)
            }
          }
          return file
        })
      }
    }
  )

  watch(
    () => props.fileList,
    (fileList: UploadFile[]) => {
      uploadFiles.value = fileList.map((file) => {
        const cloneFile = cloneDeep(file)
        return {
          ...cloneFile,
          uid: file.uid || genUid(tempIndex++),
          status: file.status || 'success',
        }
      })
    },
    {
      immediate: true,
      deep: true,
    }
  )

  return {
    abort,
    clearFiles,
    handleError,
    handleProgress,
    handleStart,
    handleSuccess,
    handleRemove,
    submit,
    uploadFiles,
    uploadRef,
  }
}
