/**
 * @module 101/defaults
 */

var isObject = require('./is-object');
var isBoolean = require('./is-boolean');
var exists = require('./exists');

/**
 * Mixes in properties from source into target when
 * the property is not a property of `target`
 * @param  {Object} [target] Mix into
 * @param  {Object} source The defaults description
 * @return {Object}        THe resulting target
 */
module.exports = defaults;

function defaults (target, source, deep) {
  if (arguments.length === 1) {
    source = target;
    return function (target) {
      return defaults(target, source);
    };
  } else if (isBoolean(source)) {
    deep = source;
    source = target;
    return function (target) {
      return defaults(target, source, deep);
    };
  }
  target = target || {};
  deep = deep || false;
  if (!source) {
    return target;
  }
  return reduceObject(target, source, deep);
}

function reduceObject (target, source, deep) {
  return Object.keys(source).reduce(function (target, key) {
    if (isObject(target[key]) && isObject(source[key]) && deep) {
      reduceObject(target[key], source[key]);
      return target;
    }
    target[key] = exists(target[key]) ? target[key] : source[key];
    return target;
  }, target);
}
