module.exports = class PlaceholderReplacer {
  static get rule () {
    return 'githubPlaceholderReplacer'
  }

  OpenAPIRoot () {
    return {
      onEnter: openapi => {
        this.placeholders = {
          ...openapi['x-githubPlaceholders'],
          apiURL: openapi.servers[0].url
        }
      },
      onExit: openapi => {
        delete openapi['x-githubPlaceholders']
      }
    }
  }

  any () {
    return {
      onEnter: node => {
        // we need to do it recursivelly as our visitor is run only for known nodes
        replacePlaceholders(node, this.placeholders)
      }
    }
  }
}

/**
 * Replaces placeholders in OpenAPI document strings.
 *
 * @param {*} node - JSON node.
 * @param {*} placeholders - Placeholders map.
 */
function replacePlaceholders (node, placeholders) {
  const replace = str => {
    for (const name of Object.keys(placeholders)) {
      const value = placeholders[name]
      str = str.replace(new RegExp(`\\\${${name}}`, 'g'), value)
    }
    return str
  }

  crawl(node, (object, key) => {
    if (typeof object[key] === 'string') {
      object[key] = replace(object[key])
    }
  })
}

/**
 * Recursively visit any object with visitor fn.
 *
 * @param {*} object - Object to crawl.
 * @param {(object: any, key: string) => void} visitor - Visitor function.
 */
function crawl (object, visitor) {
  if (typeof object !== 'object' || object == null) {
    return
  }
  for (const key of Object.keys(object)) {
    visitor(object, key)
    crawl(object[key], visitor)
  }
}
