var fs = require('fs')
var path = require('path')
var merge = require('lodash.merge')

function createConfig(basePath = undefined, envPrefix = undefined) {
  let configDefaults
  let configLocal
  let envPrefixUpper = envPrefix ? envPrefix.toUpperCase() + "_" : ''
  if (basePath) {
    configDefaults = path.join(basePath, 'config.defaults.json')
    configLocal = path.join(basePath, 'config.json')
  } else {
    configDefaults = path.join(path.dirname(require.main.filename), 'config.defaults.json')
    configLocal = path.join(path.dirname(require.main.filename), 'config.json')
  }

  let config = {}
  
  const objectDeepKeys = function (obj) {
    return Object.keys(obj).filter(key => obj[key] instanceof Object).map(key => objectDeepKeys(obj[key]).map(k => `${key}.${k}`)).reduce((x, y) => x.concat(y), Object.keys(obj))
  }
  
  const set = function(obj, path, value) {
    var schema = obj  // a moving reference to internal objects within obj
    var pList = path.split('.')
    var len = pList.length
    for(var i = 0; i < len-1; i++) {
        var elem = pList[i]
        if( !schema[elem] ) schema[elem] = {}
        schema = schema[elem]
    }
    schema[pList[len-1]] = value
  }
  const get = function(obj, path) {
    var schema = obj  // a moving reference to internal objects within obj
    var pList = path.split('.')
    var len = pList.length
    for(var i = 0; i < len-1; i++) {
        var elem = pList[i]
        if( !schema[elem] ) schema[elem] = {}
        schema = schema[elem]
    }
    return schema[pList[len-1]]
  }

  config._reload = function () {
    try {
      fs.accessSync(configDefaults)
      config = merge(this, JSON.parse(fs.readFileSync(configDefaults, 'utf8')))
    } catch(error) {
      console.log('No File ' + configDefaults)
    }
    try {
      fs.accessSync(configLocal)
      config = merge(this, JSON.parse(fs.readFileSync(configLocal, 'utf8')))
    } catch (error) {
      console.log('No File ' + configLocal)
    }
    let keys = objectDeepKeys(this)
    for (let index = 0; index < keys.length; index++) {
      const element = keys[index]
      let env = process.env[envPrefixUpper + element.toUpperCase().replace(/\./g, '_')]
      if (env) {
        env = (env == 'true') ? true : env
        env = (env == 'false') ? false : env
        set(this, element, env)
      }
    }
    for (let index = 0; index < keys.length; index++) {
      const element = keys[index]
      let value = get(config, element).toString()
      if (value.startsWith('file:')) {
        try {
          set(config, element, fs.readFileSync(value.replace('file:', ''), 'utf8'))
        } catch (error) {
          console.error(error.message)
        }
        
      }
    }
  }
  config._show = function () {
    // clone the config object
    let config = JSON.parse(JSON.stringify(this))
    // remove _reload
    delete config._reload
    // remove _show
    delete config._show
    // remove _set
    delete config._set
    // remove _get
    delete config._get
    // redact all nested objects where the key is 
    // 'password' or 'secret' or 'token' or 'key' or 'apiKey' or 'apiToken' or 'apiSecret'
    // or 'user' or 'username'
    let keys = objectDeepKeys(config)
    for (let index = 0; index < keys.length; index++) {
      const element = keys[index]
      if (element.toLowerCase().includes('password') || element.toLowerCase().includes('secret') || element.toLowerCase().includes('token') || element.toLowerCase().includes('key') || element.toLowerCase().includes('apikey') || element.toLowerCase().includes('apitoken') || element.toLowerCase().includes('apisecret') || element.toLowerCase().includes('user') || element.toLowerCase().includes('username')) {
        set(config, element, 'REDACTED')
      }
    }
    return config
  }
  config._reload()
  return config
}
module.exports = { createConfig };