const { getManifest } = require("../src")
const { getJsHelperList } = require("../src/helpers")

const {
  convertToJS,
  processStringSync,
  encodeJSBinding,
} = require("../src/index.js")

function tryParseJson(str) {
  if (typeof str !== "string") {
    return
  }

  try {
    return JSON.parse(str.replace(/'/g, '"'))
  } catch (e) {
    return
  }
}

const getParsedManifest = () => {
  const manifest = getManifest()
  const collections = Object.keys(manifest)
  const examples = collections.reduce((acc, collection) => {
    const functions = Object.entries(manifest[collection])
      .filter(([_, details]) => details.example)
      .map(([name, details]) => {
        const example = details.example
        let [hbs, js] = example.split("->").map(x => x.trim())
        if (!js) {
          // The function has no return value
          return
        }

        // Trim 's
        js = js.replace(/^'|'$/g, "")
        let parsedExpected
        if ((parsedExpected = tryParseJson(js))) {
          if (Array.isArray(parsedExpected)) {
            if (typeof parsedExpected[0] === "object") {
              js = JSON.stringify(parsedExpected)
            } else {
              js = parsedExpected.join(",")
            }
          }
        }
        const requiresHbsBody = details.requiresBlock
        return [name, { hbs, js, requiresHbsBody }]
      })
      .filter(x => !!x)

    if (Object.keys(functions).length) {
      acc[collection] = functions
    }
    return acc
  }, {})

  return examples
}
module.exports.getParsedManifest = getParsedManifest

module.exports.runJsHelpersTests = ({ funcWrap, testsToSkip } = {}) => {
  funcWrap = funcWrap || (delegate => delegate())
  const manifest = getParsedManifest()

  const processJS = (js, context) => {
    return funcWrap(() => processStringSync(encodeJSBinding(js), context))
  }

  function escapeRegExp(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") // $& means the whole matched string
  }

  describe("can be parsed and run as js", () => {
    const jsHelpers = getJsHelperList()
    const jsExamples = Object.keys(manifest).reduce((acc, v) => {
      acc[v] = manifest[v].filter(([key]) => jsHelpers[key])
      return acc
    }, {})

    describe.each(Object.keys(jsExamples))("%s", collection => {
      const examplesToRun = jsExamples[collection]
        .filter(([_, { requiresHbsBody }]) => !requiresHbsBody)
        .filter(([key]) => !testsToSkip?.includes(key))

      examplesToRun.length &&
        it.each(examplesToRun)("%s", async (_, { hbs, js }) => {
          const context = {
            double: i => i * 2,
            isString: x => typeof x === "string",
          }

          const arrays = hbs.match(/\[[^/\]]+\]/)
          arrays?.forEach((arrayString, i) => {
            hbs = hbs.replace(
              new RegExp(escapeRegExp(arrayString)),
              `array${i}`
            )
            context[`array${i}`] = JSON.parse(arrayString.replace(/'/g, '"'))
          })

          let convertedJs = convertToJS(hbs)

          let result = await processJS(convertedJs, context)
          result = result.replace(/ /g, " ")
          expect(result).toEqual(js)
        })
    })
  })
}