import R from "ramda"

export const makeAsync = (fn) => R.pipeP((...args) => Promise.resolve(args), R.apply(fn))

export const tryCatchAsync = R.curry((fn, catcher) => R.pipe(makeAsync(fn), R.invoker(1, `catch`)(catcher)))

export const pipeAsync = (...fns) => R.pipeP(makeAsync(R.nth(0, fns)), ...R.tail(fns))

export const sideEffects = (...fns) => R.pipe(R.juxt([...fns, R.identity]), R.last)

export const sideEffect = R.curryN(1)(sideEffects)

// Takes list if arrays and returns array that consists of equally-positioned pairs
// Note: The returned list is truncated to the length of the longest of the input arrays
// Examples:
// zipLongest([1,2], [10,20,30]) -> [[1,10],[2,20],[undefined,30]]
const zipLongest = R.pipe(
  R.juxt([
    R.pipe(R.unapply(R.flip(R.pluck)), R.map),
    R.pipe(R.unapply(R.map(R.length)), R.apply(Math.max), R.range(0)),
  ]),
  R.apply(R.call),
)

// Takes default value and predicate function and returns function that returns default value
// when predicate function returns true
// Examples:
// defaultToWhen(R.identity, R.complement(R.is(Function)))(null) -> R.identity
// defaultToWhen(R.identity, R.complement(R.is(Function)))(R.inc) -> R.inc
export const defaultToWhen = R.curry((defaultValue, predicate) => R.when(predicate, R.always(defaultValue)))

// Takes array of functions and array and returns array where corresponding functions
// was applied to corresponding items
// Examples:
// evolveArray([,R.inc,,R.always(50)], [1,1,1]) -> [1,2,1,undefined,50]
export const evolveArray = R.curryN(2)(
  R.pipe(
    zipLongest,
    R.map(R.pipe(R.adjust(defaultToWhen(R.identity, R.complement(R.is(Function))), 0), R.apply(R.call))),
  ),
)
