import nodeConfig from './config.js'
const isEmpty = (data) => data === null || data === undefined || data === ''
const isEmptyArray = (data) => (Array.isArray(data) ? data.length === 0 : true)

export class NodeUtils {
  /**
   * 根据自增数生成64进制id
   * @returns 64进制id字符串
   */
  static idGenerator() {
    let qutient = new Date() - new Date('2020-08-01')
    qutient += Math.ceil(Math.random() * 1000) // 防止重複
    const chars = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz'
    const charArr = chars.split('')
    const radix = chars.length
    const res = []
    do {
      let mod = qutient % radix
      qutient = (qutient - mod) / radix
      res.push(charArr[mod])
    } while (qutient)
    return res.join('')
  }

  /**
   * 判断节点类型
   * @param {Node} node - 节点数据
   * @returns Boolean
   */
  static isConditionNode(node) {
    return node && node.type === 'condition'
  }
  static isCopyNode(node) {
    return node && node.type === 'copy'
  }
  static isStartNode(node) {
    return node && node.type === 'start'
  }
  static isApproverNode(node) {
    return node && node.type === 'approver'
  }
  static isTimerNode(node) {
    return node && node.type === 'timer'
  }

  static isMailNode(node) {
    return node && node.type === 'mail'
  }

  static isServiceTaskNode(node) {
    return node && node.type === 'serviceTask'
  }

  static isDingNode(node) {
    return node && node.type === 'ding'
  }

  static isDxNode(node) {
    return node && node.type === 'dx'
  }

  static isWxNode(node) {
    return node && node.type === 'wx'
  }

  static isZhanNode(node) {
    return node && node.type === 'zhan'
  }

  static isScriptTaskNode(node) {
    return node && node.type === 'scriptTask'
  }
  static isShellTaskNode(node) {
    return node && node.type === 'shellTask'
  }

  static isHttpTaskNode(node) {
    return node && node.type === 'httpTask'
  }

  static isSubFlowNode(node) {
    return node && node.type === 'subFlow'
  }
  static isInterflowNode(node) {
    return node && node.type === 'approver' && node.isInterflow
  }
  /**
   * 创建指定节点
   * @param { String } type - 节点类型
   * @param { Object } previousNodeId - 父节点id
   * @returns { Object } 节点数据
   */
  static createNode(type, previousNodeId) {
    let res = JSON.parse(JSON.stringify(nodeConfig[type]))
    res.nodeId = this.idGenerator()
    res.prevId = previousNodeId
    return res
  }
  /**
   * 获取指定节点的父节点（前一个节点）
   * @param { String } prevId - 父节点id
   * @param { Object } processData - 流程图全部数据
   * @returns { Object } 父节点
   */
  static getPreviousNode(prevId, processData) {
    if (processData.nodeId === prevId) return processData
    if (processData.childNode) {
      let r1 = this.getPreviousNode(prevId, processData.childNode)
      if (r1) {
        return r1
      }
    }
    if (processData.conditionNodes) {
      for (let c of processData.conditionNodes) {
        let r2 = this.getPreviousNode(prevId, c)
        if (r2) {
          return r2
        }
      }
    }
  }

  /**
   * 删除节点
   * @param { Object  } nodeData - 被删除节点的数据
   * @param { Object  } processData - 流程图的所有节点数据
   */
  static deleteNode(nodeData, processData, checkEmpty = true) {
    let prevNode = this.getPreviousNode(nodeData.prevId, processData)
    if (checkEmpty && prevNode.type === 'empty') {
      if (this.isConditionNode(nodeData) || this.isInterflowNode(nodeData)) {
        const willDelBranch = prevNode.conditionNodes.length === 2
        const target = willDelBranch ? prevNode : nodeData
        this.deleteNode(target, processData, willDelBranch)
      } else {
        if (isEmptyArray(prevNode.conditionNodes)) {
          this.deleteNode(prevNode, processData)
        }
        this.deleteNode(nodeData, processData, false)
      }
      // this.deleteNode( prevNode, processData )
      // !this.isConditionNode(nodeData) && this.deleteNode(nodeData, processData)
      return
    }
    let concatChild = (prev, delNode) => {
      prev.childNode = delNode.childNode
      isEmptyArray(prev.conditionNodes) && (prev.conditionNodes = delNode.conditionNodes)
      prev.childNode && (prev.childNode.prevId = prev.nodeId)
      prev.conditionNodes && prev.conditionNodes.forEach((c) => (c.prevId = prev.nodeId))
    }
    if (this.isConditionNode(nodeData) || this.isInterflowNode(nodeData)) {
      let cons = prevNode.conditionNodes
      let index = cons.findIndex((c) => c.nodeId === nodeData.nodeId)
      if (cons.length > 2) {
        cons.splice(index, 1)
      } else {
        let anotherCon = cons[+!index]
        delete prevNode.conditionNodes
        delete prevNode.conditionType
        if (prevNode.childNode) {
          let endNode = anotherCon
          while (endNode.childNode) {
            endNode = endNode.childNode
          }
          endNode.childNode = prevNode.childNode
          endNode.childNode.prevId = endNode.nodeId
        }
        concatChild(prevNode, anotherCon)
        if (prevNode.childNode && prevNode.childNode.type === 'empty') {
          this.deleteNode(prevNode.childNode, prevNode)
        }
      }
      // 重新编排优先级
      cons.forEach((c, i) => (c.properties.priority = i))
      return
    }
    concatChild(prevNode, nodeData)
  }
  // TODO:
  // static copyNode ( nodeData, processData ) {
  //   let prevNode = this.getPreviousNode( nodeData.prevId, processData )
  //   let index = prevNode.conditionNodes.findIndex( c => c.nodeId === nodeData.nodeId )

  // }
  /**
   * 添加审计节点（普通节点 approver）
   * @param { Object } data - 目标节点数据，在该数据节点之后添加审计节点
   * @param { Object } isBranchAction - 目标节点数据，是否是条件分支
   * @param { Object } newChildNode - 传入的新的节点 用户操作均为空  删除操作/添加抄送人 会传入该参数 以模拟添加节点
   */
  static addApprovalNode(data, isBranchAction, newChildNode = undefined) {
    let oldChildNode = data.childNode
    newChildNode = newChildNode || this.createNode('approver', data.nodeId)
    data.childNode = newChildNode
    if (oldChildNode) {
      newChildNode.childNode = oldChildNode
      oldChildNode.prevId = newChildNode.nodeId
    }
    let conditionNodes = data.conditionNodes
    if (Array.isArray(conditionNodes) && !isBranchAction && conditionNodes.length) {
      newChildNode.conditionNodes = conditionNodes.map((c) => {
        c.prevId = newChildNode.nodeId
        return c
      })
      delete data.conditionNodes
    }
    if (oldChildNode && oldChildNode.type === 'empty' && newChildNode.type !== 'empty' && oldChildNode.conditionNodes.length === 0) {
      this.deleteNode(oldChildNode, data)
    }
  }
  /**
   * 添加空节点
   * @param { Object } data - 空节点的父级节点
   * @return { Object } emptyNode - 空节点数据
   */
  static addEmptyNode(data) {
    let emptyNode = this.createNode('empty', data.nodeId)
    this.addApprovalNode(data, true, emptyNode)
    return emptyNode
  }

  static addCopyNode(data, isBranchAction) {
    // 复用addApprovalNode  因为抄送人和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('copy', data.nodeId))
  }

  static addTimerNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('timer', data.nodeId))
  }

  static addMailNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('mail', data.nodeId))
  }

  static addServiceTaskNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('serviceTask', data.nodeId))
  }
  static addHttpTaskNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('httpTask', data.nodeId))
  }

  static addDingNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('ding', data.nodeId))
  }
  static addZhanNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('zhan', data.nodeId))
  }
  static addDxNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('dx', data.nodeId))
  }
  static addWxNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('wx', data.nodeId))
  }

  static addScriptTaskNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('scriptTask', data.nodeId))
  }

  static addShellTaskNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('shellTask', data.nodeId))
  }

  static addSubFlowNode(data, isBranchAction) {
    // 复用addApprovalNode  因为和审批人基本一致
    this.addApprovalNode(data, isBranchAction, this.createNode('subFlow', data.nodeId))
  }
  /**
   * 添加条件节点 condition 通过点击添加条件进入该操作
   * @param { Object } data - 目标节点所在分支数据，在该分支最后添加条件节点
   */
  static appendConditionNode(data) {
    const conditions = data.conditionNodes
    let node = this.createNode('condition', data.nodeId)
    let defaultNodeIndex = conditions.findIndex((node) => node.properties.isDefault)
    node.properties.priority = conditions.length
    if (defaultNodeIndex > -1) {
      conditions.splice(-1, 0, node) // 插在倒数第二个
      //更新优先级
      node.properties.priority = conditions.length - 2
      conditions[conditions.length - 1].properties.priority = conditions.length - 1
    } else {
      conditions.push(node)
    }
    this.setDefaultCondition(node, data)
  }
  static appendInterflowNode(data) {
    const conditions = data.conditionNodes
    let node = this.createNode('interflow', data.nodeId)
    let defaultNodeIndex = conditions.findIndex((node) => node.properties.isDefault)
    node.properties.priority = conditions.length
    if (defaultNodeIndex > -1) {
      conditions.splice(-1, 0, node) // 插在倒数第二个
      //更新优先级
      node.properties.priority = conditions.length - 2
      conditions[conditions.length - 1].properties.priority = conditions.length - 1
    } else {
      conditions.push(node)
    }
  }
  /**
   * 添加条件分支 branch
   * @param { Object } data - 目标节点所在节点数据，在该节点最后添加分支节点
   */
  static appendBranch(data, isBottomBtnOfBranch) {
    // isBottomBtnOfBranch 用户点击的是分支树下面的按钮
    let nodeData = data
    // 由于conditionNodes是数组 不能添加下级分支 故在两个分支树之间添加一个不会显示的正常节点 兼容此种情况
    if (Array.isArray(data.conditionNodes) && data.conditionNodes.length) {
      if (isBottomBtnOfBranch) {
        // 添加一个模拟用的空白节点并返回这个节点，作为新分支的父节点
        nodeData = this.addEmptyNode(nodeData, true)
      } else {
        let emptyNode = this.addEmptyNode(nodeData, true)
        emptyNode.conditionNodes = nodeData.conditionNodes
        emptyNode.conditionType = 'condition'
        emptyNode.conditionNodes.forEach((n) => {
          n.prevId = emptyNode.nodeId
        })
      }
    }
    let conditionNodes = [this.createNode('condition', nodeData.nodeId), this.createNode('condition', nodeData.nodeId)].map((c, i) => {
      c.properties.priority = i
      return c
    })
    nodeData.conditionNodes = conditionNodes
    nodeData.conditionType = 'condition'
  }
  static appendInterflowBranch(data, isBottomBtnOfBranch) {
    // isBottomBtnOfBranch 用户点击的是分支树下面的按钮
    let nodeData = data
    // 由于conditionNodes是数组 不能添加下级分支 故在两个分支树之间添加一个不会显示的正常节点 兼容此种情况
    if (Array.isArray(data.conditionNodes) && data.conditionNodes.length) {
      if (isBottomBtnOfBranch) {
        // 添加一个模拟用的空白节点并返回这个节点，作为新分支的父节点
        nodeData = this.addEmptyNode(nodeData, true)
      } else {
        let emptyNode = this.addEmptyNode(nodeData, true)
        emptyNode.conditionNodes = nodeData.conditionNodes
        emptyNode.conditionType = 'interflow'
        emptyNode.conditionNodes.forEach((n) => {
          n.prevId = emptyNode.nodeId
        })
      }
    }
    let conditionNodes = [this.createNode('interflow', nodeData.nodeId), this.createNode('interflow', nodeData.nodeId)]
    nodeData.conditionNodes = conditionNodes
    nodeData.conditionType = 'interflow'
  }
  /**
   * 重设节点优先级（条件节点）
   * @param {Node} cnode - 当前节点
   * @param {Number} oldPriority - 替换前的优先级（在数组中的顺序）
   * @param {Node} processData - 整个流程图节点数据
   */
  static resortPrioByCNode(cnode, oldPriority, processData) {
    // 当前节点为默认节点 取消修改优先级
    if (cnode.properties.isDefault) {
      cnode.properties.priority = oldPriority
      return
    }
    let prevNode = this.getPreviousNode(cnode.prevId, processData)
    let newPriority = cnode.properties.priority
    // 替换节点为默认节点 取消修改优先级
    if (prevNode.conditionNodes[newPriority].properties.isDefault) {
      cnode.properties.priority = oldPriority
      return
    }
    let delNode = prevNode.conditionNodes.splice(newPriority, 1, cnode)[0]
    delNode.properties.priority = oldPriority
    prevNode.conditionNodes[oldPriority] = delNode
  }

  /**
   * 提升条件节点优先级——排序在前
   * @param { Object } data - 目标节点数据
   * @param { Object  } processData - 流程图的所有节点数据
   */
  static increasePriority(data, processData) {
    if (data.properties.isDefault) {
      // 默认节点不能修改优先级
      return
    }
    // 分支节点数据 包含该分支所有的条件节点
    let prevNode = this.getPreviousNode(data.prevId, processData)
    let branchData = prevNode.conditionNodes
    let index = branchData.findIndex((c) => c === data)
    if (index) {
      // 和前一个数组项交换位置 Array.prototype.splice会返回包含被删除的项的集合（数组）
      branchData[index - 1].properties.priority = index
      branchData[index].properties.priority = index - 1
      branchData[index - 1] = branchData.splice(index, 1, branchData[index - 1])[0]
    }
  }
  /**
   * 降低条件节点优先级——排序在后
   * @param { Object } data - 目标节点数据
   * @param { Object  } processData - 流程图的所有节点数据
   */
  static decreasePriority(data, processData) {
    // 分支节点数据 包含该分支所有的条件节点
    let prevNode = this.getPreviousNode(data.prevId, processData)
    let branchData = prevNode.conditionNodes
    let index = branchData.findIndex((c) => c.nodeId === data.nodeId)
    if (index < branchData.length - 1) {
      let lastNode = branchData[index + 1]
      if (lastNode.properties.isDefault) {
        // 默认节点不能修改优先级
        return
      }
      // 和后一个数组项交换位置 Array.prototype.splice会返回包含被删除的项的集合（数组）
      lastNode.properties.priority = index
      branchData[index].properties.priority = index + 1
      branchData[index + 1] = branchData.splice(index, 1, branchData[index + 1])[0]
    }
  }
  /**
   * 当有其他条件节点设置条件后 检查并设置最后一个节点为默认节点
   * @param {Node} cnode  - 当前节点
   * @param {Node} processData - 整个流程图节点数据或父级节点数据
   */
  static setDefaultCondition(cnode, processData) {
    const DEFAULT_TEXT = '其他情况进入此流程'
    const conditions = this.getPreviousNode(cnode.prevId, processData).conditionNodes
    const hasCondition = (node) => node.properties && (node.properties.initiator || !isEmptyArray(node.properties.conditions))
    const clearDefault = (node) => {
      node.properties.isDefault = false
      node.content === DEFAULT_TEXT && (node.content = '请设置条件')
    }
    const setDefault = (node) => {
      node.properties.isDefault = true
      node.content = DEFAULT_TEXT
    }
    let count = 0
    conditions.slice(0, -1).forEach((node) => {
      hasCondition(node) && count++
      clearDefault(node)
    })
    const lastNode = conditions[conditions.length - 1]
    count > 0 && !hasCondition(lastNode) ? setDefault(lastNode) : clearDefault(lastNode)
  }
  /**
   * 校验单个节点必填项完整性
   * @param {Node} node - 节点数据
   */
  static checkNode(node, parent) {
    let valid = true
    const props = node.properties
    this.isStartNode(node) && !props.initiator && (valid = false)

    this.isConditionNode(node) && !props.isDefault && isEmptyArray(props.conditions) && (valid = false)

    this.isSubFlowNode(node) && (!props.flowId || (props.initiateType === 1 && isEmptyArray(props.initiator) && isEmptyArray(props.initiatePos) && isEmptyArray(props.initiateRole))) && (valid = false)

    const customSettings = [6, 8]
    this.isApproverNode(node) &&
      (!props.assigneeType || customSettings.includes(props.assigneeType)) &&
      isEmptyArray(props.approvers) &&
      isEmptyArray(props.approverPos) &&
      isEmptyArray(props.approverRole) &&
      (valid = false)
    return valid
  }
  /**
   * 判断所有节点是否信息完整
   * @param {Node} processData - 整个流程图数据
   * @returns {Boolean}
   */
  static checkAllNode(processData) {
    let valid = true
    const loop = (node, callback, parent) => {
      !this.checkNode(node, parent) && callback()
      if (node.childNode) loop(node.childNode, callback, parent)
      if (!isEmptyArray(node.conditionNodes)) {
        node.conditionNodes.forEach((n) => loop(n, callback, node))
      }
    }
    loop(processData, () => (valid = false))
    return valid
  }
}

/**
 * 添模拟数据
 */
export function getMockData() {
  let startNode = NodeUtils.createNode('start')
  startNode.childNode = NodeUtils.createNode('approver', startNode.nodeId)
  return startNode
}
