import Utils from './utils'
class $Object {
  constructor(AV, className, defaultACL, request, that) {
    this.AV = AV
    this.$http = className ? new AV.Object(className) : AV.Object
    this.defaultACL = defaultACL
    this.request = request
    this.that = that
    this.avObject = null
  }

  // 保存
  save() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () => (avObject ? avObject.save() : $http.save())
    return request.call(that, dispatchRequest)
  }

  saveAll(objects) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.saveAll(objects)
    return request.call(that, dispatchRequest)
  }

  destroy() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () =>
      avObject ? avObject.destroy() : $http.destroy()
    return request.call(that, dispatchRequest)
  }

  destroyAll(objectIds) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.destroyAll(objectIds)
    return request.call(that, dispatchRequest)
  }

  fetch() {
    const { $http, avObject, request, that } = this
    const dispatchRequest = () => (avObject ? avObject.fetch() : $http.fetch())
    return request.call(that, dispatchRequest)
  }

  fetchAll(objectIds) {
    const { AV, request, that } = this
    const dispatchRequest = () => AV.Object.fetchAll(objectIds)
    return request.call(that, dispatchRequest)
  }

  increment(key, count) {
    const { $http, avObject } = this
    const http = avObject || $http
    this.avObject = request.increment(key, count)
    return this
  }

  extend(classname) {
    if (!classname) return null
    return this.$http.extend(classname)
  }

  getUsers() {
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.getUsers()
    this.avObject = request
    return this
  }

  add(key, item) {
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.add(key, item)
    this.avObject = request
    return this
  }

  createWithoutData(className, objectId) {
    const { AV, avObject, $http } = this
    this.avObject = AV.Object.createWithoutData(className, objectId)
    return this
  }

  set(jsonObj) {
    let request = this.$http
    if (this.avObject) request = this.avObject
    for (const k in jsonObj) {
      request.set(k, jsonObj[k])
    }
    this.avObject = request
    return this
  }

  unset(keys) {
    let request = this.$http
    if (!keys || !keys.length) return
    if (this.avObject) request = this.avObject
    keys.forEach((item) => request.unset(item))
    this.avObject = request
    return this
  }

  setACL(acl) {
    if (!acl) return
    let request = this.$http
    if (this.avObject) request = this.avObject
    request.setACL(acl)
    this.avObject = request
    return this
  }

  // 个人定制API

  async handleData(data, { $pointType, $acl }) {
    const { AV, $http, avObject, that, defaultACL } = this
    const TYPE = $pointType
    const ACL = $acl
    const jsonObj = {
      ...data,
    }
    const setData = {}
    // acl对象
    if (defaultACL) this.setACL(defaultACL)
    Object.keys(jsonObj).forEach((item) => {
      if (
        jsonObj[item] &&
        (!Utils.isPlainObject(jsonObj[item]) || !jsonObj[item][TYPE]) &&
        item !== ACL
      ) {
        setData[item] = jsonObj[item]
      } else if (jsonObj[ACL] && Utils.isPlainObject(jsonObj[ACL])) {
        const aclObj = that.ACL().createACL({ ...jsonObj[ACL] }).avObject
        this.setACL(aclObj)
      }
    })
    // point指针对象
    const pointObjs = Object.keys(jsonObj)
      .filter(
        (item) => Utils.isPlainObject(jsonObj[item]) && jsonObj[item][TYPE]
      )
      .map((item) => ({
        [item]: jsonObj[item],
      }))
    // point指针搜索项
    const pointObjKeys = pointObjs.map((item) => Object.keys(item)[0])
    const findDatas = await Promise.all(
      pointObjs.map(async (item, index) => {
        const value = item[pointObjKeys[index]]
        const conditionKey = Object.keys(value).filter((item) => item !== TYPE)
        const qdata = new AV.Query(value[TYPE])
        const point = await qdata
          .equalTo([conditionKey[0]], value[conditionKey[0]])
          .first()
        return {
          key: pointObjKeys[index],
          point,
        }
      })
    )
    const pointDatas = findDatas
      .filter((item) => item.point)
      .map((item) => ({
        key: item.key,
        point: AV.Object.createWithoutData(item.point.className, item.point.id),
        className: item.point.className,
        id: item.point.id,
      }))
    pointDatas.forEach((item) => (setData[item.key] = item.point))
    return setData
  }

  /**
   * 构建或更新数据库行数据对象 (行对象)
   * @param {Array} objects  需要保存的数据对象数组
   * @param {String} $objectId 需要修改的对象id属性名
   * @param {String} $class pointer的class名称
   * @param {String} $acl acl的属性名称
   * @return {Array} 创建好的LeanCloud数据对象数组
   */
  async setData(
    objects,
    { $objectId = 'objectId', $pointType = 'pointType', $acl = 'acl' } = {}
  ) {
    if (!objects && objects.length) return
    const OBJECT_ID = $objectId
    const TYPE = $pointType
    const ACL = $acl
    const { AV, $http, avObject, that } = this
    const { className } = $http
    const AVObjcets = await Promise.all(
      objects.map(async (item) => {
        const hasId = item[OBJECT_ID]
        const data = hasId ? item.data : item
        const request = hasId
          ? this.createWithoutData(className, hasId).avObject
          : $http
        const setData = await this.handleData(data, {
          $pointType: TYPE,
          $acl: ACL,
        })
        request.set({ ...setData })
        return request
      })
    )
    return this.saveAll(AVObjcets)
  }
  /**
   * 批量删除数据库行数据对象或对象属性 (行对象)
   * @param {Array} objectIds 需要删除的数据对象id数组
   * @return null 返回为空
   */
  deleteData(objectIds) {
    const { AV, $http } = this
    const points = []
    const { className = '' } = $http
    if (!className) return
    if (!objectIds || !objectIds.length) return
    objectIds.forEach((item) =>
      points.push(AV.Object.createWithoutData(className, item))
    )
    return this.destroyAll(points)
  }
}

export default $Object
