import * as React from "react";
import * as R from "ramda";

export const isNotNil = R.compose(R.not, R.isNil);

export const isAnyNil = R.any(R.isNil);

export const isNilOrEmpty = R.either(R.isNil, R.isEmpty);

export const isNotEmpty = R.complement(R.isEmpty);

export const withoutNil = R.reject(R.isNil);

/**
 * tells wether a React Component is a class component or not
 * @param {ReactComponent} Component to test
 * @returns {Boolean}
 */
export const isClassComponent = R.allPass([
  isNotNil,
  R.propSatisfies(isNotNil, "prototype"),
  R.pathSatisfies(R.is(Function), ["prototype", "render"]),
]);

/**
 * attaches a lifecycle method to a React class Component
 * will preserve existing methods, and work for all hooks except constructor
 * curried function to be called with f(Component)(method, name)
 * @param {ReactComponent} Component to attach methods onto
 * @param {Function} method to attach
 * @param {String} name of the lifecycle method to attach
 * @returns {ReactComponent}
 */
export function attachMethodToPrototype(Component) {
  return function (method, name) {
    const existingMethod = Component.prototype[name];

    Component.prototype[name] = function (...args) {
      if (existingMethod && R.is(Function, existingMethod)) {
        existingMethod.apply(this, args);
      }

      method.apply(this, args);
    };

    return Component;
  };
}

/**
 * Wraps a stateless React component in a class component
 * @param {Object} Component to wrap in a class
 * @returns {ReactComponent}
 */
export function wrapInClass(Component) {
  return class WrappedWithLifeCycleMethod extends React.Component {
    render() {
      return <Component {...this.props} />;
    }
  };
}

/**
 * Attaches all the provided lifecycle methods to a React class component
 * curried function to be called with f(lifecycleMethods)(Component)
 * @param {Object} lifecycleMethods map where the key is the lifecycle hook, and the value is the method
 * @param {ReactComponent} Component to wrap and attach the lifecycle methods
 * @returns {ReactComponent}
 */
export function addLifeCycleMethods(lifecycleMethods) {
  return function (Component) {
    R.mapObjIndexed(attachMethodToPrototype(Component), lifecycleMethods);

    return Component;
  };
}
