text: const { drop, dropRight, first, last } = require('lodash')
  
  // ----- START LIQUID PATTERNS ----- //
  const startTag = /(?:^ *?)?{%-?/
  const endTag = /-?%}/
  const contentRegex = /[\s\S]*?/gm
  const emptyString = /^\s*?$/m
  const nonEmptyString = /^.*?\S.*?/m
  const ifStatement = new RegExp(startTag.source + ' if .*?' + endTag.source)
  const ifRegex = new RegExp(ifStatement.source, 'gm')
  const firstIfRegex = new RegExp(ifStatement.source, 'm')
  const elseRegex = new RegExp(startTag.source + ' else ?' + endTag.source)
  const endRegex = new RegExp(startTag.source + ' endif ?' + endTag.source, 'g')
  const captureEndRegex = new RegExp('(' + endRegex.source + ')', 'g')
  const dropSecondEndIf = new RegExp('(' + endRegex.source + contentRegex.source + ')' + endRegex.source, 'm')
  const inlineEndRegex = new RegExp(nonEmptyString.source + endRegex.source, 'gm')
  const inlineIfRegex = new RegExp(nonEmptyString.source + ifStatement.source, 'gm')
  
  // include one level of nesting
  const liquidBlockRegex = new RegExp(ifRegex.source + '((' + ifRegex.source + contentRegex.source + endRegex.source + '|[\\s\\S])*?)' + endRegex.source, 'gm')
  const elseBlockRegex = new RegExp(elseRegex.source + '(?:' + ifRegex.source + contentRegex.source + endRegex.source + '|[\\s\\S])*?' + endRegex.source, 'gm')
  // ----- END LIQUID PATTERNS ----- //
  
  module.exports = function removeLiquidStatements (content, versionToDeprecate, nextOldestVersion) {
    // see tests/fixtures/remove-liquid-statements for examples
    const regexes = {
      // remove liquid only
      greaterThanVersionToDeprecate: new RegExp(startTag.source + ` if ?(?: currentVersion == ('|")'?free-pro-team@latest'?('|")  ?or)? currentVersion ver_gt ('|")${versionToDeprecate}('|") ` + endTag.source, 'gm'),
      andGreaterThanVersionToDeprecate1: new RegExp('(' + startTag.source + ` if .*?) and currentVersion ver_gt (?:'|")${versionToDeprecate}(?:'|")( ` + endTag.source + ')', 'gm'),
      andGreaterThanVersionToDeprecate2: new RegExp('(' + startTag.source + ` if )currentVersion ver_gt (?:'|")${versionToDeprecate}(?:'|") and (.*? ` + endTag.source + ')', 'gm'),
      notEqualsVersionToDeprecate: new RegExp('(' + startTag.source + ` if)(?:( currentVersion .*?) or)? currentVersion != (?:'|")${versionToDeprecate}(?:'|")( ` + endTag.source + ')', 'gm'),
      andNotEqualsVersionToDeprecate: new RegExp('(' + startTag.source + ` if .*?) and currentVersion != (?:'|")${versionToDeprecate}(?:'|")( ` + endTag.source + ')', 'gm'),
      // remove liquid and content
      lessThanNextOldestVersion: new RegExp(startTag.source + ` if .*?ver_lt ('|")${nextOldestVersion}('|") ?` + endTag.source, 'm'),
      equalsVersionToDeprecate: new RegExp(startTag.source + ` if .*?== ('|")${versionToDeprecate}('|") ?` + endTag.source, 'm')
    }
  
    let allLiquidBlocks = getLiquidBlocks(content)
    if (!allLiquidBlocks) return content
  
    // remove markup like ver_gt "2.13" and leave content
    content = removeLiquidOnly(content, allLiquidBlocks, regexes)
  
    // get latest liquidBlocks based on updated content
    allLiquidBlocks = getLiquidBlocks(content)
  
    if (allLiquidBlocks) {
      // remove content and surrounding markup like ver_lt "2.14" and == "2.13"
      content = removeLiquidAndContent(content, allLiquidBlocks, regexes)
    }
  
    // clean up
    content = removeExtraNewlines(content)
  
    return content
  }
  
  function getLiquidBlocks (content) {
    const liquidBlocks = content.match(liquidBlockRegex)
    if (!liquidBlocks) return
  
    // handle up to one level of nesting
    const innerBlocks = getInnerBlocks(liquidBlocks)
  
    return innerBlocks ? liquidBlocks.concat(innerBlocks) : liquidBlocks
  }
  
  function getInnerBlocks (liquidBlocks) {
    const innerBlocks = []
  
    liquidBlocks.forEach(block => {
      const ifStatements = block.match(ifRegex)
      if (!ifStatements) return
  
      // only process blocks that have more than one if statement
      if (!(ifStatements.length > 1)) return
  
      // drop first character so we don't match outer if statement
      // because it's already included in array of blocks
      const newBlock = block.slice(1)
      const innerIfStatements = newBlock.match(liquidBlockRegex)
  
      // add each inner if statement to array of blocks
      innerIfStatements.forEach(innerIfStatement => {
        innerBlocks.push(innerIfStatement)
      })
    })
  
    return innerBlocks
  }
  
  function removeLiquidOnly (content, allLiquidBlocks, regexes) {
    const blocksToUpdate = allLiquidBlocks
      .filter(block => {
        // inner blocks are processed separately, so we only care about first if statements
        const firstIf = block.match(firstIfRegex)
        if (block.match(regexes.greaterThanVersionToDeprecate)) return firstIf[0] === block.match(regexes.greaterThanVersionToDeprecate)[0]
        if (block.match(regexes.andGreaterThanVersionToDeprecate1)) return firstIf[0] === block.match(regexes.andGreaterThanVersionToDeprecate1)[0]
        if (block.match(regexes.andGreaterThanVersionToDeprecate2)) return firstIf[0] === block.match(regexes.andGreaterThanVersionToDeprecate2)[0]
        if (block.match(regexes.notEqualsVersionToDeprecate)) return firstIf[0] === block.match(regexes.notEqualsVersionToDeprecate)[0]
        if (block.match(regexes.andNotEqualsVersionToDeprecate)) return firstIf[0] === block.match(regexes.andNotEqualsVersionToDeprecate)[0]
      })
  
    blocksToUpdate.forEach(block => {
      let newBlock = block
  
      if (newBlock.match(regexes.andGreaterThanVersionToDeprecate1)) {
        newBlock = newBlock.replace(regexes.andGreaterThanVersionToDeprecate1, matchAndStatement1)
      }
  
      if (newBlock.match(regexes.andGreaterThanVersionToDeprecate2)) {
        newBlock = newBlock.replace(regexes.andGreaterThanVersionToDeprecate2, matchAndStatement2)
      }
  
      if (newBlock.match(regexes.notEqualsVersionToDeprecate)) {
        newBlock = newBlock.replace(regexes.notEqualsVersionToDeprecate, matchNotEqualsStatement)
      }
  
      if (newBlock.match(regexes.andNotEqualsVersionToDeprecate)) {
        newBlock = newBlock.replace(regexes.andNotEqualsVersionToDeprecate, matchAndNotEqualsStatement)
      }
  
      // replace else block with endif
      const elseBlock = getElseBlock(newBlock)
  
      if (elseBlock) {
        newBlock = newBlock.replace(`${elseBlock}`, '{% endif %}')
      }
  
      if (newBlock.match(regexes.greaterThanVersionToDeprecate) || newBlock.match(regexes.notEqualsVersionToDeprecate)) {
        newBlock = newBlock.replace(liquidBlockRegex, matchGreaterThan)
      }
  
      // we need more context to determine whether an if tag is inline or not
      const startIndex = content.indexOf(block)
      const endIndex = startIndex + block.length
      const leftIndex = startIndex - 10
      const blockWithLeftContext = content.slice(leftIndex, endIndex)
  
      // only remove newlines around non-inline tags
      if (blockWithLeftContext.match(inlineIfRegex) && block.match(inlineEndRegex)) {
        newBlock = removeLastNewline(newBlock)
      } else if (blockWithLeftContext.match(inlineIfRegex) && !block.match(inlineEndRegex)) {
        newBlock = removeLastNewline(newBlock)
      } else if (!blockWithLeftContext.match(inlineIfRegex) && block.match(inlineEndRegex)) {
        newBlock = removeFirstNewline(newBlock)
      } else {
        newBlock = removeFirstAndLastNewlines(newBlock)
      }
  
      // final replacement
      content = content.replace(block, newBlock)
    })
  
    return content
  }
  
  function matchGreaterThan (match, p1) {
    return p1
  }
  
  function matchAndStatement1 (match, p1, p2) {
    return p1 + p2
  }
  
  function matchAndStatement2 (match, p1, p2) {
    return p1 + p2
  }
  
  function matchNotEqualsStatement (match, p1, p2, p3) {
    if (!p2) return match
    return p1 + p2 + p3
  }
  
  function matchAndNotEqualsStatement (match, p1, p2) {
    return p1 + p2
  }
  
  function removeLiquidAndContent (content, allLiquidBlocks, regexes) {
    const blocksToRemove = allLiquidBlocks
      .filter(block => {
        const firstIf = block.match(firstIfRegex)
        if (block.match(regexes.lessThanNextOldestVersion)) return firstIf[0] === block.match(regexes.lessThanNextOldestVersion)[0]
        if (block.match(regexes.equalsVersionToDeprecate)) return firstIf[0] === block.match(regexes.equalsVersionToDeprecate)[0]
      })
  
    blocksToRemove.forEach(block => {
      const elseBlock = getElseBlock(block)
  
      // remove else conditionals but leave content
      if (elseBlock) {
        const numberOfEndTags = elseBlock[0].match(captureEndRegex)
  
        // remove the endif as part of removing the else block
        // if there are two endifs block, only remove the second one
        // this applies specifically to example8 in less-than-next-oldest fixture
        let newBlock
        if (numberOfEndTags.length > 1) {
          const blockWithFirstElseRemoved = elseBlock[0].replace(elseRegex, '')
          newBlock = blockWithFirstElseRemoved.replace(dropSecondEndIf, '$1')
        } else {
          newBlock = elseBlock[0].replace(elseRegex, '').replace(captureEndRegex, '')
        }
        content = content.replace(block, newBlock.trim())
      } else {
        content = content.replace(block, '')
      }
    })
  
    return content
  }
  
  function removeFirstNewline (block) {
    const lines = block.split(/\r?\n/)
    if (!first(lines).match(emptyString)) return block
    return drop(lines, 1).join('\n')
  }
  
  function removeLastNewline (block) {
    const lines = block.split(/\r?\n/)
    if (!last(lines).match(emptyString)) return block
    return dropRight(lines, 1).join('\n')
  }
  
  function removeFirstAndLastNewlines (block) {
    block = removeFirstNewline(block)
    return removeLastNewline(block)
  }
  
  function getElseBlock (block) {
    const firstIf = block.match(firstIfRegex)
    const elseBlock = block.match(elseBlockRegex)
  
    // if no else block, return the null result
    if (!elseBlock) return elseBlock
  
    // if there is an else block, check for an inner block
    const blockWithFirstIfRemoved = block.replace(firstIf[0], '')
    const innerBlock = blockWithFirstIfRemoved.match(liquidBlockRegex)
  
    // if no inner block, we can safely return the else block as is
    if (!innerBlock) return elseBlock
  
    // if there is an inner block, check if it is contained by the else block
    // if so, we can safely return the else block as is
    // see example8 in less-than-next-oldest
    if (elseBlock[0].includes(innerBlock[0])) return block.match(elseBlockRegex)
  
    // if the inner block contains the else block,
    // drop the inner block and return any other else blocks
    // see example7 in greater-than fixtures
    if (innerBlock[0].includes(elseBlock[0])) {
      const newBlock = block.replace(innerBlock[0], '')
      return newBlock.match(elseBlockRegex)
    }
  
    // otherwise, return any else matches on the original block
    return block.match(elseBlockRegex)
  }
  
  function removeExtraNewlines (content) {
    return content.replace(/(\r?\n){3,4}/gm, '\n\n')
  }
  