import Vue from 'vue'
import VueToast from 'vant/lib/toast/Toast'
import { isObject, isServer } from 'vant/lib/utils'
import { removeNode } from 'vant/lib/utils/dom/node'

const defaultOptions = {
  icon: '',
  type: 'text',
  // @deprecated
  mask: false,
  value: true,
  message: '',
  className: '',
  overlay: false,
  onClose: null,
  onOpened: null,
  duration: 2000,
  iconPrefix: undefined,
  position: 'middle',
  transition: 'van-fade',
  forbidClick: false,
  loadingType: undefined,
  getContainer: 'body',
  overlayStyle: null,
  closeOnClick: false,
  closeOnClickOverlay: false
}

// default options of specific type
let defaultOptionsMap = {}

let queue = []
let multiple = false
let currentOptions = {
  ...defaultOptions
}

function parseOptions(message) {
  if (isObject(message)) {
    return message
  }

  return { message }
}

function createInstance() {
  /* istanbul ignore if */
  if (isServer) {
    return {}
  }

  if (!queue.length || multiple) {
    const toast = new (Vue.extend(VueToast))({
      el: document.createElement('div')
    })

    toast.$on('input', (value) => {
      toast.value = value
    })

    queue.push(toast)
  }

  return queue[queue.length - 1]
}

// transform toast options to popup props
function transformOptions(options) {
  return {
    ...options,
    overlay: options.mask || options.overlay,
    mask: undefined,
    duration: undefined
  }
}

function Toast(options = {}) {
  const toast = createInstance()
  // should add z-index if previous toast has not disappeared
  // if (toast.value) {
  toast.updateZIndex()
  // }

  options = parseOptions(options)
  options = {
    ...currentOptions,
    ...defaultOptionsMap[options.type || currentOptions.type],
    ...options
  }

  options.clear = () => {
    toast.value = false

    if (options.onClose) {
      options.onClose()
    }

    if (multiple && !isServer) {
      toast.$on('closed', () => {
        clearTimeout(toast.timer)
        queue = queue.filter((item) => item !== toast)

        removeNode(toast.$el)
        toast.$destroy()
      })
    }
  }

  Object.assign(toast, transformOptions(options))
  clearTimeout(toast.timer)

  if (options.duration > 0) {
    toast.timer = setTimeout(() => {
      toast.clear()
    }, options.duration)
  }

  return toast
}

const createMethod = (type) => (options) =>
  Toast({
    type,
    ...parseOptions(options)
  });

['loading', 'success', 'fail'].forEach((method) => {
  Toast[method] = createMethod(method)
})

Toast.clear = (all) => {
  if (queue.length) {
    if (all) {
      queue.forEach((toast) => {
        toast.clear()
      })
      queue = []
    } else if (!multiple) {
      queue[0].clear()
    } else {
      queue.shift().clear()
    }
  }
}

Toast.setDefaultOptions = (type, options) => {
  if (typeof type === 'string') {
    defaultOptionsMap[type] = options
  } else {
    Object.assign(currentOptions, type)
  }
}

Toast.resetDefaultOptions = (type) => {
  if (typeof type === 'string') {
    defaultOptionsMap[type] = null
  } else {
    currentOptions = { ...defaultOptions }
    defaultOptionsMap = {}
  }
}

Toast.allowMultiple = (value = true) => {
  multiple = value
}

Toast.install = () => {
  Vue.use(VueToast)
}

Vue.prototype.$toast = Toast

export default Toast
