/**
 * @description helper fns
 * @author wangfupeng
 */

import Uppy, { UppyFile } from '@uppy/core'
import { IDomEditor } from '@wangeditor/editor'
import { createUploader } from '@wangeditor/editor'
import { AttachmentElement } from '../custom-types'
import { IUploadConfigForAttachment } from './config'

function getUploadAttachmentMenuConfig(editor: IDomEditor): IUploadConfigForAttachment {
  // 获取配置，见 `./config.js`
  return editor.getMenuConfig('uploadAttachment') as IUploadConfigForAttachment
}

/**
 * 插入 attachment 节点
 * @param fileName fileName
 * @param link link
 */
export function insertAttachment(editor: IDomEditor, fileName: string, link: string) {
  if (!fileName || !link) return

  // 还原选区
  editor.restoreSelection()

  // 插入节点
  const attachmentElem: AttachmentElement = {
    type: 'attachment',
    fileName,
    link,
    children: [{ text: '' }],
  }
  editor.insertNode(attachmentElem)
  editor.move(1)

  // 回调
  const { onInsertedAttachment } = getUploadAttachmentMenuConfig(editor)
  if (onInsertedAttachment) onInsertedAttachment(attachmentElem)
}

/**
 * 上传附件文件
 * @param editor editor
 * @param files files
 */
export async function uploadAttachments(editor: IDomEditor, files: FileList | null) {
  if (files == null) return
  const fileList = Array.prototype.slice.call(files)

  // 获取菜单配置
  const { customUpload } = getUploadAttachmentMenuConfig(editor)

  // 按顺序上传
  for await (const file of fileList) {
    // 上传
    if (customUpload) {
      // 自定义上传
      await customUpload(file, (fileName: string, link: string) =>
        insertAttachment(editor, fileName, link)
      )
    } else {
      // 默认上传
      await uploadFile(editor, file)
    }
  }
}

/**
 * 上传文件
 * @param editor editor
 * @param file file
 */
async function uploadFile(editor: IDomEditor, file: File) {
  const uppy = getUppy(editor)

  const { name, type, size } = file
  uppy.addFile({
    name,
    type,
    size,
    data: file,
  })
  await uppy.upload()
}

// 存储 editor uppy 的关系 - 缓存 uppy ，不重复创建
const EDITOR_TO_UPPY_MAP = new WeakMap<IDomEditor, Uppy>()

/**
 * 获取 uppy 实例（并通过 editor 缓存）
 * @param editor editor
 */
function getUppy(editor: IDomEditor): Uppy {
  // 从缓存中获取
  let uppy = EDITOR_TO_UPPY_MAP.get(editor)
  if (uppy != null) return uppy

  const menuConfig = getUploadAttachmentMenuConfig(editor)
  const { onSuccess, onProgress, onFailed, customInsert, onError } = menuConfig

  // 上传完成之后
  const successHandler = (file: UppyFile, res: any) => {
    // 预期 res 格式：
    // 成功：{ errno: 0, data: { url } }
    // 失败：{ errno: !0, message: '失败信息' }

    if (customInsert) {
      // 用户自定义插入文件，此时 res 格式可能不符合预期
      customInsert(res, file, (fileName: string, link: string) =>
        insertAttachment(editor, fileName, link)
      )
      return
    }

    const { errno = 1, data = {} } = res
    if (errno !== 0) {
      console.error(`'${file.name}' upload failed`, res)

      // failed 回调
      onFailed(file, res)
      return
    }

    const { url = '' } = data
    insertAttachment(editor, file.name, url)

    // success 回调
    onSuccess(file, res)
  }

  // progress 显示进度条
  const progressHandler = (progress: number) => {
    editor.showProgressBar(progress)

    // 回调函数
    onProgress && onProgress(progress)
  }

  // onError 提示错误
  const errorHandler = (file: any, err: any, res: any) => {
    const fileName = file.name
    console.error(`'${fileName} upload error`, err, res)

    // 回调函数
    onError && onError(file, err, res)
  }

  // 创建 uppy
  uppy = createUploader({
    ...menuConfig,
    onProgress: progressHandler,
    onSuccess: successHandler,
    onError: errorHandler,
  })
  // 缓存 uppy
  EDITOR_TO_UPPY_MAP.set(editor, uppy)

  return uppy
}
