const path = require('path')
const expect = require('chai').expect
const html = require('html')
const htmlBeautifier = require('js-beautify').html
const { JSDOM } = require('jsdom')

const nunjucksConfig = require('~/config/nunjucks')

const nunjucks = nunjucksConfig(null, {
  root: path.normalize(rootPath),
})

const COMPONENTS_PATH = '_components/'
const MACROS_PATH = '_macros/'
const EXT = 'njk'

const normaliseHtml = (string) => {
  const beautiful = htmlBeautifier(string, {
    indent_size: 2,
    max_preserve_newlines: 0,
  })

  return html.prettyPrint(beautiful, {
    indent_size: 2,
  })
}

function getMacros (fileName) {
  const filePath = `${MACROS_PATH}${fileName}.${EXT}`

  return {
    render (macroName, props, ...rest) {
      const importString = `{% from "${filePath}" import ${macroName} %}`
      const args = rest.length ? `,${[...rest].map(JSON.stringify)}` : ''
      const macroOutput = nunjucks.renderString(`${importString} {{ ${macroName}(${JSON.stringify(props)}${args}) }}`)
      return normaliseHtml(macroOutput)
    },

    renderToDom (macroName, props, ...rest) {
      const macroOutput = this.render(macroName, props, ...rest)
      return (new JSDOM(macroOutput)).window.document.body.firstElementChild
    },
  }
}

const renderComponent = (name, input) => {
  const componentPath = `${COMPONENTS_PATH}${name}.${EXT}`
  return normaliseHtml(nunjucks.render(componentPath, input))
}

const renderComponentToDom = (name, input) => {
  const renderedComponent = renderComponent(name, input)
  return (new JSDOM(renderedComponent)).window.document.body.firstElementChild
}

const expectComponent = (name, input, expected) => {
  // Normalise HTML whitespace, to make diffing simpler
  expect(
    renderComponent(name, input)
  ).to.equal(
    normaliseHtml(expected)
  )
}

function domTokenToArray (obj) {
  let array = []
  // iterate backwards ensuring that length is an UInt32
  for (let i = obj.length >>> 0; i--;) {
    array[i] = obj[i]
  }
  return array
}

module.exports = {
  expectComponent,
  getMacros,
  renderComponentToDom,
  domTokenToArray,
}

@@ -693,39 +693,43 @@
 %0A%0A  
-return %7B%0A   
+function
  render
- (m
+M
 acro
-N
+ (n
 ame,
+ caller,
  pro
@@ -735,34 +735,32 @@
 ops, ...rest) %7B%0A
-  
     const import
@@ -800,22 +800,17 @@
 mport $%7B
-macroN
+n
 ame%7D %25%7D%60
@@ -810,26 +810,24 @@
 me%7D %25%7D%60%0A    
- 
- 
 const args =
@@ -882,31 +882,64 @@
 %7D%60 : ''%0A    
-  const
+let macroOutput%0A%0A    if (caller) %7B%0A     
  macroOutput
@@ -964,16 +964,25 @@
 String(%60
+%0A        
 $%7Bimport
@@ -992,20 +992,222 @@
 ing%7D
- %7B%7B $%7BmacroN
+%0A        %7B%25 call $%7Bname%7D($%7BJSON.stringify(props, undefined, 1)%7D$%7Bargs%7D) %25%7D%0A          $%7Bcaller.join(' ')%7D%0A        %7B%25 endcall %25%7D%0A      %60)%0A    %7D else %7B%0A      macroOutput = nunjucks.renderString(%60$%7BimportString%7D %7B%7B $%7Bn
 ame%7D
@@ -1243,24 +1243,29 @@
 gs%7D) %7D%7D%60)%0A  
+  %7D%0A%0A
     return n
@@ -1296,201 +1296,770 @@
 )%0A  
-  %7D,%0A%0A    renderToDom (macroName, props, ...rest) %7B%0A      const macroOutput = this.render(macroName, props, ...rest)%0A      return (new JSDOM(macroOutput)).window.document.body.firstElementChild
+%7D%0A%0A  return %7B%0A    render (macroName, props, ...rest) %7B%0A      return renderMacro(macroName, null, props, ...rest)%0A    %7D,%0A%0A    renderWithCaller (macroName, caller, props, ...rest) %7B%0A      return renderMacro(macroName, caller, props, ...rest)%0A    %7D,%0A%0A    renderToDom (macroName, props, ...rest) %7B%0A      const macroOutput = this.render(macroName, props, ...rest)%0A      return (new JSDOM(macroOutput)).window.document.body.firstElementChild%0A    %7D,%0A%0A    renderWithCallerToDom (macroName, props, ...rest) %7B%0A      return function (...caller) %7B%0A        if (!caller.length) %7B return null %7D%0A        const macroOutput = this.renderWithCaller(macroName, caller, props, ...rest)%0A        return (new JSDOM(macroOutput)).window.document.body.firstElementChild%0A      %7D.bind(this)
 %0A   

Refactor macro test helper to test callers