import {__, applyTo, cond, curry, has, is, keys, T} from 'ramda'
import {safeSpecTransforms, isPlainObj, objectify} from './util'
/**
* A port of [Ramda's evolve()](http://ramdajs.com/docs/#evolve), but any
* primitive value or array in the transform spec is wrapped in a function that
* always returns that value. Any functions already on the transform spec are
* applied at the prop-level (like evolve() always does) and any objects on the
* transform spec call the evolver recursively (again, normal behavior for
* evolve()).
* In other words, the only difference between this and Ramda's
* evolve() function is that primitives and array vales are wrapped in an
* always() function before the spec is fed to evolve() rather than being
* omitted from the final output.
*
* @func
* @sig {k: (a -> b)} -> {k: v} -> {k: v}
* @param {Object} transforms An object with transform functions or nested transform
* functions or primitive vales to be wrapped with a function.
* @param {Object} input The input object to pass through prop-level transformations
* @returns {Object} The modified input object, with any prop-level transforms
* applied to it
*/
const evolveSpec = curry((transforms, input) => {
const result = {}
const spec = safeSpecTransforms(transforms)
const object = objectify(input)
keys(object).forEach(key => {
result[key] = cond([
[is(Function), applyTo(object[key])],
[isPlainObj, evolveSpec(__, object[key])],
[T, val => (has(key, spec) ? val : object[key])]
])(spec[key])
})
return result
})
export default evolveSpec