import { getCurrentInstance, nextTick, onMounted, watch } from 'vue'

import { vmHasRouter } from '../../utils/private.vm/vm.js'

export const useModelToggleProps = {
  modelValue: {
    type: Boolean,
    default: null
  },

  'onUpdate:modelValue': [Function, Array]
}

export const useModelToggleEmits = ['beforeShow', 'show', 'beforeHide', 'hide']

// handleShow/handleHide -> removeTick(), self (& emit show)

export default function useModelToggle({
  showing,
  canShow, // optional
  hideOnRouteChange, // optional
  handleShow, // optional
  handleHide, // optional
  handleRouteChange, // optional
  processOnMount // optional
}) {
  const vm = getCurrentInstance()
  const { props, emit, proxy } = vm

  let payload

  function toggle(evt) {
    if (showing.value) {
      hide(evt)
    } else {
      show(evt)
    }
  }

  function show(evt) {
    if (
      props.disable ||
      evt?.qAnchorHandled === true ||
      (canShow !== void 0 && !canShow(evt))
    ) {
      return
    }

    const listener = props['onUpdate:modelValue'] !== void 0

    if (listener && !__QUASAR_SSR_SERVER__) {
      emit('update:modelValue', true)
      payload = evt
      nextTick(() => {
        if (payload === evt) {
          payload = void 0
        }
      })
    }

    if (__QUASAR_SSR_SERVER__ || props.modelValue === null || !listener) {
      processShow(evt)
    }
  }

  function processShow(evt) {
    if (showing.value) return

    showing.value = true
    emit('beforeShow', evt)

    if (handleShow !== void 0) {
      handleShow(evt)
    } else {
      emit('show', evt)
    }
  }

  function hide(evt) {
    if (__QUASAR_SSR_SERVER__ || props.disable) return

    const listener = props['onUpdate:modelValue'] !== void 0

    if (listener && !__QUASAR_SSR_SERVER__) {
      emit('update:modelValue', false)
      payload = evt
      nextTick(() => {
        if (payload === evt) {
          payload = void 0
        }
      })
    }

    if (__QUASAR_SSR_SERVER__ || props.modelValue === null || !listener) {
      processHide(evt)
    }
  }

  function processHide(evt) {
    if (!showing.value) return

    showing.value = false

    emit('beforeHide', evt)

    if (handleHide !== void 0) {
      handleHide(evt)
    } else {
      emit('hide', evt)
    }
  }

  function processModelChange(val) {
    if (props.disable && val) {
      if (props['onUpdate:modelValue'] !== void 0) {
        emit('update:modelValue', false)
      }
    } else if ((val === true) !== showing.value) {
      const fn = val ? processShow : processHide
      fn(payload)
    }
  }

  watch(() => props.modelValue, processModelChange)

  if (hideOnRouteChange !== void 0 && vmHasRouter(vm)) {
    watch(
      () => proxy.$route.fullPath,
      () => {
        if (hideOnRouteChange.value && showing.value) {
          handleRouteChange?.()
          hide()
        }
      }
    )
  }

  if (processOnMount) {
    onMounted(() => {
      processModelChange(props.modelValue)
    })
  }

  // expose public methods
  const publicMethods = { show, hide, toggle }
  Object.assign(proxy, publicMethods)

  return publicMethods
}
