import {
  isFn,
  cached,
  camelize,
  hasOwn
} from 'uni-shared'

import {
  handleLink as handleBaseLink
} from '../../../mp-weixin/runtime/wrapper/util'

import deepEqual from './deep-equal'

const customizeRE = /:/g

const customize = cached((str) => {
  return camelize(str.replace(customizeRE, '-'))
})

export const isComponent2 = my.canIUse('component2')

export const mocks = ['$id']

export function initRefs () {

}

export function initBehavior ({
  properties
}) {
  const props = {}

  Object.keys(properties).forEach(key => {
    props[key] = properties[key].value
  })

  return {
    props
  }
}

export function initRelation (detail) {
  this.props.onVueInit(detail)
}

export function initSpecialMethods (mpInstance) {
  if (!mpInstance.$vm) {
    return
  }
  let path = mpInstance.is || mpInstance.route
  if (!path) {
    return
  }
  if (path.indexOf('/') === 0) {
    path = path.substr(1)
  }
  const specialMethods = my.specialMethods && my.specialMethods[path]
  if (specialMethods) {
    specialMethods.forEach(method => {
      if (isFn(mpInstance.$vm[method])) {
        mpInstance[method] = function (event) {
          if (hasOwn(event, 'markerId')) {
            event.detail = typeof event.detail === 'object' ? event.detail : {}
            event.detail.markerId = event.markerId
          }
          // TODO normalizeEvent
          mpInstance.$vm[method](event)
        }
      }
    })
  }
}

export function initChildVues (mpInstance) {
  // 此时需保证当前 mpInstance 已经存在 $vm
  if (!mpInstance.$vm) {
    return
  }
  mpInstance._$childVues && mpInstance._$childVues.forEach(({
    vuePid,
    vueOptions,
    VueComponent,
    mpInstance: childMPInstance
  }) => {
    // 父子关系
    handleBaseLink.call(mpInstance, {
      detail: {
        vuePid,
        vueOptions
      }
    })

    childMPInstance.$vm = new VueComponent(vueOptions)

    initSpecialMethods(childMPInstance)

    handleRef.call(vueOptions.parent.$scope, childMPInstance)

    childMPInstance.$vm.$mount()

    initChildVues(childMPInstance)

    childMPInstance.$vm._isMounted = true
    childMPInstance.$vm.__call_hook('mounted')
    childMPInstance.$vm.__call_hook('onReady')
  })

  delete mpInstance._$childVues
}

export function handleProps (ref) {
  const eventProps = {}
  let refProps = ref.props
  const eventList = (refProps['data-event-list'] || '').split(',')
  // 初始化支付宝小程序组件事件
  eventList.forEach(key => {
    const handler = refProps[key]
    const res = key.match(/^on([A-Z])(\S*)/)
    const event = res && (res[1].toLowerCase() + res[2])
    refProps[key] = eventProps[key] = function () {
      const props = Object.assign({}, refProps)
      props[key] = handler
      // 由于支付宝事件可能包含多个参数，不使用微信小程序事件格式
      delete props['data-com-type']
      triggerEvent.bind({ props })(event, {
        __args__: [...arguments]
      })
    }
  })
  // 处理 props 重写
  Object.defineProperty(ref, 'props', {
    get () {
      return refProps
    },
    set (value) {
      refProps = Object.assign(value, eventProps)
    }
  })
}

export function handleRef (ref) {
  if (!(ref && this.$vm)) {
    return
  }
  const refName = ref.props['data-ref']
  const refInForName = ref.props['data-ref-in-for']
  if (refName) {
    this.$vm.$refs[refName] = ref.$vm || ref
  } else if (refInForName) {
    (this.$vm.$refs[refInForName] || (this.$vm.$refs[refInForName] = [])).push(ref.$vm || ref)
  }
}

export function triggerEvent (type, detail, options) {
  const handler = this.props && this.props[customize('on-' + type)]
  if (!handler) {
    return
  }

  const eventOpts = this.props['data-event-opts']
  const eventParams = this.props['data-event-params']
  const comType = this.props['data-com-type']

  const target = {
    dataset: {
      eventOpts,
      eventParams,
      comType
    }
  }

  handler({
    type: customize(type),
    target,
    currentTarget: target,
    detail
  })
}

const IGNORES = ['$slots', '$scopedSlots']

export function createObserver (isDidUpdate) {
  return function observe (props) {
    const prevProps = isDidUpdate ? props : this.props
    const nextProps = isDidUpdate ? this.props : props
    if (deepEqual(prevProps, nextProps)) {
      return
    }
    Object.keys(prevProps).forEach(name => {
      if (IGNORES.indexOf(name) === -1) {
        const prevValue = prevProps[name]
        const nextValue = nextProps[name]
        if (!isFn(prevValue) && !isFn(nextValue) && !deepEqual(prevValue, nextValue)) {
          this.$vm[name] = nextProps[name]
        }
      }
    })
  }
}

export const handleLink = (function () {
  if (isComponent2) {
    return function handleLink (detail) {
      return handleBaseLink.call(this, {
        detail
      })
    }
  }
  return function handleLink (detail) {
    if (this.$vm && this.$vm._isMounted) { // 父已初始化
      return handleBaseLink.call(this, {
        detail: {
          vuePid: detail.vuePid,
          vueOptions: detail.vueOptions
        }
      })
    }
    // 支付宝通过 didMount 来实现，先子后父，故等父 ready 之后，统一初始化
    (this._$childVues || (this._$childVues = [])).unshift(detail)
  }
})()

export const handleWrap = function (mp, destory) {
  const vueId = mp.props.vueId
  const list = (mp.props['data-event-list'] || '').split(',')
  list.forEach(eventName => {
    const key = `${eventName}${vueId}`
    if (destory) {
      delete this[key]
    } else {
      this[key] = function () {
        mp.props[eventName].apply(this, arguments)
      }
    }
  })
  if (!destory) {
    handleProps(mp)
  }
}
