'use strict'

function _resolveEscapeSequences (value) {
  return value.replace(/\\\$/g, '$')
}

function expandValue (value, processEnv, runningParsed) {
  const env = { ...runningParsed, ...processEnv } // process.env wins

  const regex = /(?<!\\)\${([^{}]+)}|(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)/g

  let result = value
  let match
  const seen = new Set() // self-referential checker

  while ((match = regex.exec(result)) !== null) {
    seen.add(result)

    const [template, bracedExpression, unbracedExpression] = match
    const expression = bracedExpression || unbracedExpression

    // match the operators `:+`, `+`, `:-`, and `-`
    const opRegex = /(:\+|\+|:-|-)/
    // find first match
    const opMatch = expression.match(opRegex)
    const splitter = opMatch ? opMatch[0] : null

    const r = expression.split(splitter)

    let defaultValue
    let value

    const key = r.shift()

    if ([':+', '+'].includes(splitter)) {
      defaultValue = env[key] ? r.join(splitter) : ''
      value = null
    } else {
      defaultValue = r.join(splitter)
      value = env[key]
    }

    if (value) {
      // self-referential check
      if (seen.has(value)) {
        result = result.replace(template, defaultValue)
      } else {
        result = result.replace(template, value)
      }
    } else {
      result = result.replace(template, defaultValue)
    }

    // if the result equaled what was in process.env and runningParsed then stop expanding
    if (result === runningParsed[key]) {
      break
    }

    regex.lastIndex = 0 // reset regex search position to re-evaluate after each replacement
  }

  return result
}

function expand (options) {
  // for use with progressive expansion
  const runningParsed = {}

  let processEnv = process.env
  if (options && options.processEnv != null) {
    processEnv = options.processEnv
  }

  // dotenv.config() ran before this so the assumption is process.env has already been set
  for (const key in options.parsed) {
    let value = options.parsed[key]

    // short-circuit scenario: process.env was already set prior to the file value
    if (processEnv[key] && processEnv[key] !== value) {
      value = processEnv[key]
    } else {
      value = expandValue(value, processEnv, runningParsed)
    }

    options.parsed[key] = _resolveEscapeSequences(value)

    // for use with progressive expansion
    runningParsed[key] = _resolveEscapeSequences(value)
  }

  for (const processKey in options.parsed) {
    processEnv[processKey] = options.parsed[processKey]
  }

  return options
}

module.exports.expand = expand
