import {
  isObject
} from 'uni-shared'

export const mocks = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__']

export function findVmByVueId (vm, vuePid) {
  const $children = vm.$children
  // 优先查找直属(反向查找:https://github.com/dcloudio/uni-app/issues/1200)
  for (let i = $children.length - 1; i >= 0; i--) {
    const childVm = $children[i]
    if (childVm.$scope._$vueId === vuePid) {
      return childVm
    }
  }
  // 反向递归查找
  let parentVm
  for (let i = $children.length - 1; i >= 0; i--) {
    parentVm = findVmByVueId($children[i], vuePid)
    if (parentVm) {
      return parentVm
    }
  }
}

export function initBehavior (options) {
  return Behavior(options)
}

export function isPage () {
  return !!this.route
}

export function initRelation (detail) {
  this.triggerEvent('__l', detail)
}

function selectAllComponents (mpInstance, selector, $refs) {
  const components = mpInstance.selectAllComponents(selector) || []
  components.forEach(component => {
    const ref = component.dataset.ref
    $refs[ref] = component.$vm || toSkip(component)
    if (__PLATFORM__ === 'mp-weixin') {
      if (component.dataset.vueGeneric === 'scoped') {
        component.selectAllComponents('.scoped-ref').forEach(scopedComponent => {
          selectAllComponents(scopedComponent, selector, $refs)
        })
      }
    }
  })
}

export function syncRefs (refs, newRefs) {
  const oldKeys = new Set(...Object.keys(refs))
  const newKeys = Object.keys(newRefs)
  newKeys.forEach(key => {
    const oldValue = refs[key]
    const newValue = newRefs[key]
    if (Array.isArray(oldValue) && Array.isArray(newValue) && oldValue.length === newValue.length && newValue.every(value => oldValue.includes(value))) {
      return
    }
    refs[key] = newValue
    oldKeys.delete(key)
  })
  oldKeys.forEach(key => {
    delete refs[key]
  })
  return refs
}

export function initRefs (vm) {
  const mpInstance = vm.$scope
  const refs = {}
  Object.defineProperty(vm, '$refs', {
    get () {
      const $refs = {}
      selectAllComponents(mpInstance, '.vue-ref', $refs)
      // TODO 暂不考虑 for 中的 scoped
      const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for') || []
      forComponents.forEach(component => {
        const ref = component.dataset.ref
        if (!$refs[ref]) {
          $refs[ref] = []
        }
        $refs[ref].push(component.$vm || toSkip(component))
      })
      return syncRefs(refs, $refs)
    }
  })
}

export function handleLink (event) {
  const {
    vuePid,
    vueOptions
  } = event.detail || event.value // detail 是微信,value 是百度(dipatch)

  let parentVm

  if (vuePid) {
    parentVm = findVmByVueId(this.$vm, vuePid)
  }

  if (!parentVm) {
    parentVm = this.$vm
  }

  vueOptions.parent = parentVm
}

export function markMPComponent (component) {
  // 在 Vue 中标记为小程序组件
  const IS_MP = '__v_isMPComponent'
  Object.defineProperty(component, IS_MP, {
    configurable: true,
    enumerable: false,
    value: true
  })
  return component
}

export function toSkip (obj) {
  const OB = '__ob__'
  const SKIP = '__v_skip'
  if (isObject(obj) && Object.isExtensible(obj)) {
    // 避免被 @vue/composition-api 观测
    Object.defineProperty(obj, OB, {
      configurable: true,
      enumerable: false,
      value: {
        [SKIP]: true
      }
    })
  }
  return obj
}
