import * as R from "ramda";

export const filterObj = R.curry((pred, obj) =>
  R.compose(R.fromPairs, R.filter(R.apply(pred)), R.toPairs)(obj)
);

export const mapKeys = R.curry((fn, obj) =>
  R.fromPairs(R.map(R.adjust(0, fn), R.toPairs(obj)))
);

export const rejectItemByPath = (path, prop, object) =>
  R.compose(R.reject(R.pathEq(path, prop)), R.values)(object);

export const objFromListWith = R.curry((fn, list) =>
  R.chain(R.zipObj, R.map(fn))(list)
);

export const partitionByKeys = R.curry((fn, obj) =>
  R.compose(R.map(R.fromPairs), R.partition(fn), R.toPairs)(obj)
);

const isObject = R.compose(R.equals("Object"), R.type);
const allAreObjects = R.compose(R.all(isObject), R.values);
const hasLeft = R.has("left");
const hasRight = R.has("right");
const hasBoth = R.both(hasLeft, hasRight);
const isEqual = R.both(hasBoth, R.compose(R.apply(R.equals), R.values));

const markAdded = R.compose(R.append(undefined), R.values);
const markRemoved = R.compose(R.prepend(undefined), R.values);
const isAddition = R.both(hasLeft, R.complement(hasRight));
const isRemoval = R.both(R.complement(hasLeft), hasRight);

// eslint-disable-next-line @typescript-eslint/no-use-before-define
export const objectDiff = R.curry(_diff);

function _diff(l, r) {
  return R.compose(
    R.map(
      R.cond([
        [isAddition, markAdded],
        [isRemoval, markRemoved],
        [
          hasBoth,
          R.ifElse(
            allAreObjects,
            R.compose(R.apply(objectDiff), R.values),
            R.values
          ),
        ],
      ])
    ),
    R.reject(isEqual),
    R.useWith(R.mergeWith(R.merge), [
      R.map(R.objOf("left")),
      R.map(R.objOf("right")),
    ])
  )(l, r);
}
