import { isString, isObject, isNumber, isNull, isInteger, isEmpty, isBoolean } from 'lodash-es'
const isArray = Array.isArray

class Convert {
  constructor() {
    this._option = {
      $id: 'http://example.com/root.json',
      $schema: 'http://json-schema.org/draft-07/schema#'
    }
    this._object = null
  }

  /**
   * 转换函数
   * @param {*} object 需要转换的对象
   * @param {*} ?option 可选参数，目前只有能设置 root 节点的 $id 和 $schema
   */
  format(object, option = {}) {
    // 数据校验，确保传入的的object只能是对象或数组
    if (!isObject(object)) {
      throw new TypeError('传入参数只能是对象或数组')
    }
    // 合并属性
    this._option = Object.assign(this._option, option)
    // 需要转换的对象
    this._object = object
    let convertRes
    // 数组类型和对象类型结构不一样
    if (isArray(object)) {
      convertRes = this._arrayToSchema()
    } else {
      convertRes = this._objectToSchema()
    }
    // 释放
    this._object = null
    return convertRes
  }

  /**
   * 数组类型转换成JSONSCHEMA
   */
  _arrayToSchema() {
    // root节点基本信息
    const result = this._value2object(this._object, this._option.$id, '', true)
    if (this._object.length > 0) {
      const itemArr = []
      for (let index = 0; index < this._object.length; index++) {
        // 创建items对象的基本信息
        const objectItem = this._object[index]
        let item = this._value2object(objectItem, `#/items`, 'items')
        if (isObject(objectItem) && !isEmpty(objectItem)) {
          // 递归遍历
          const objectItemSchema = this._json2schema(objectItem, `#/items`)
          // 合并对象
          item = Object.assign(item, objectItemSchema)
        }
        itemArr.push(item)
      }
      result['items'] = itemArr
    }
    return result
  }

  /**
   * 对象类型转换成JSONSCHEMA
   */
  _objectToSchema() {
    let baseResult = this._value2object(this._object, this._option.$id, '', true)
    const objectSchema = this._json2schema(this._object)
    baseResult = Object.assign(baseResult, objectSchema)
    return baseResult
  }

  _json2schema(object, name = '') {
    // 如果递归值不是对象，那么return掉
    if (!isObject(object)) {
      return
    }
    // 处理当前路径$id
    if (name === '' || name === undefined) {
      name = '#'
    }
    const result = {}
    // 判断传入object是对象还是数组。
    if (isArray(object)) {
      result.items = {}
    } else {
      result.properties = {}
    }
    // 遍历传入的对象
    for (const key in object) {
      if (Object.prototype.hasOwnProperty.call(object, key)) {
        const element = object[key]
        // 如果只是undefined。跳过
        if (element === undefined) {
          continue
        }
        const $id = `${name}/properties/${key}`
        // 判断当前 element 的值 是否也是对象，如果是就继续递归，不是就赋值给result
        if (!result['properties']) {
          continue
        }
        if (isObject(element)) {
          // 创建当前属性的基本信息
          result['properties'][key] = this._value2object(element, $id, key)
          if (isArray(element)) {
            // 针对空数组和有值的数组做不同处理
            if (element.length > 0) {
              // 是数组
              const itemArr = []
              for (let index = 0; index < element.length; index++) {
                const elementItem = element[index]
                // 创建items对象的基本信息
                let item = this._value2object(elementItem, `${$id}/items`, key + 'items')
                // 判断第一项是否是对象,且对象属性不为空
                if (isObject(elementItem) && !isEmpty(elementItem)) {
                  // 新增的properties才合并进来
                  item = Object.assign(item, this._json2schema(elementItem, `${$id}/items`))
                }
                itemArr.push(item)
              }
              result['properties'][key]['items'] = itemArr
            }
          } else {
            // 不是数组，递归遍历获取，然后合并对象属性
            result['properties'][key] = Object.assign(
              result['properties'][key],
              this._json2schema(element, $id)
            )
          }
        } else {
          // 一般属性直接获取基本信息
          if (result['properties']) {
            result['properties'][key] = this._value2object(element, $id, key)
          }
        }
      }
    }
    return result
  }

  /**
   * 把json的值转换成对象类型
   * @param {*} value
   * @param {*} $id
   * @param {*} key
   */
  _value2object(value, $id, key = '', root = false) {
    const objectTemplate = {
      $id: $id,
      title: `The ${key} Schema`,
      mock: {
        mock: value
      }
    }

    // 判断是否为初始化root数据
    if (root) {
      objectTemplate['$schema'] = this._option.$schema
      objectTemplate['title'] = `The Root Schema`
      objectTemplate['mock'] = undefined
    }
    if (isBoolean(value)) {
      objectTemplate.type = 'boolean'
    } else if (isInteger(value)) {
      objectTemplate.type = 'integer'
    } else if (isNumber(value)) {
      objectTemplate.type = 'number'
    } else if (isString(value)) {
      objectTemplate.type = 'string'
    } else if (isNull(value)) {
      objectTemplate.type = 'null'
    } else if (isArray(value)) {
      objectTemplate.type = 'array'
      objectTemplate['mock'] = undefined
    } else if (isObject(value)) {
      objectTemplate.type = 'object'
      objectTemplate['mock'] = undefined
    }

    return objectTemplate
  }
}
export default Convert
