const { getTable } = require("../api/controllers/table/utils")
const {
  findHBSBlocks,
  decodeJSBinding,
  isJSBinding,
  encodeJSBinding,
} = require("@budibase/string-templates")

/**
 * When values are input to the system generally they will be of type string as this is required for template strings.
 * This can generate some odd scenarios as the Schema of the automation requires a number but the builder might supply
 * a string with template syntax to get the number from the rest of the context. To support this the server has to
 * make sure that the post template statement can be cast into the correct type, this function does this for numbers
 * and booleans.
 *
 * @param {object} inputs An object of inputs, please note this will not recurse down into any objects within, it simply
 * cleanses the top level inputs, however it can be used by recursively calling it deeper into the object structures if
 * the schema is known.
 * @param {object} schema The defined schema of the inputs, in the form of JSON schema. The schema definition of an
 * automation is the likely use case of this, however validate.js syntax can be converted closely enough to use this by
 * wrapping the schema properties in a top level "properties" object.
 * @returns {object} The inputs object which has had all the various types supported by this function converted to their
 * primitive types.
 */
exports.cleanInputValues = (inputs, schema) => {
  if (schema == null) {
    return inputs
  }
  for (let inputKey of Object.keys(inputs)) {
    let input = inputs[inputKey]
    if (typeof input !== "string") {
      continue
    }
    let propSchema = schema.properties[inputKey]
    if (!propSchema) {
      continue
    }
    if (propSchema.type === "boolean") {
      let lcInput = input.toLowerCase()
      if (lcInput === "true") {
        inputs[inputKey] = true
      }
      if (lcInput === "false") {
        inputs[inputKey] = false
      }
    }
    if (propSchema.type === "number") {
      let floatInput = parseFloat(input)
      if (!isNaN(floatInput)) {
        inputs[inputKey] = floatInput
      }
    }
  }
  return inputs
}

/**
 * Given a row input like a save or update row we need to clean the inputs against a schema that is not part of
 * the automation but is instead part of the Table/Table. This function will get the table schema and use it to instead
 * perform the cleanInputValues function on the input row.
 *
 * @param {string} tableId The ID of the Table/Table which the schema is to be retrieved for.
 * @param {object} row The input row structure which requires clean-up after having been through template statements.
 * @returns {Promise<Object>} The cleaned up rows object, will should now have all the required primitive types.
 */
exports.cleanUpRow = async (tableId, row) => {
  let table = await getTable(tableId)
  return exports.cleanInputValues(row, { properties: table.schema })
}

exports.getError = err => {
  if (err == null) {
    return "No error provided."
  }
  if (
    typeof err === "object" &&
    (err.toString == null || err.toString() === "[object Object]")
  ) {
    return JSON.stringify(err)
  }
  return typeof err !== "string" ? err.toString() : err
}

exports.substituteLoopStep = (hbsString, substitute) => {
  let blocks = []
  let checkForJS = isJSBinding(hbsString)
  if (checkForJS) {
    hbsString = decodeJSBinding(hbsString)
    blocks.push(hbsString)
  } else {
    blocks = findHBSBlocks(hbsString)
  }
  for (let block of blocks) {
    block = block.replace(/loop/, substitute)
    if (checkForJS) {
      hbsString = encodeJSBinding(block)
    } else {
      hbsString = block
    }
  }

  return hbsString
}

exports.stringSplit = value => {
  if (value == null) {
    return []
  }
  if (value.split("\n").length > 1) {
    value = value.split("\n")
  } else {
    value = value.split(",")
  }
  return value
}
