import * as R from "ramda";
import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";

import { functionForName } from "./MappingFunctions";
import { resolveColor } from "./utils";

/**
 * Uses passed function or retrieves function according to func name,
 * and performs the function on the entry with the provided args.
 * @param {Object} entry    Single entry of a feed
 * @param {String | Function} func     Func name or custom function
 * @param {Array} args      Additional arguments for the function
 */
function retrieveData(entry, func, args) {
  if (R.isNil(func)) {
    throw new Error(
      "Missing func in the data mapping, please revise or update cell styles configuration"
    );
  }

  const funcToUse = isFunction(func) ? func : functionForName(func);

  return funcToUse(entry, args);
}

/**
 * Inflates the provided masterCell configuration with the data coming from a datasource entry
 * curried function of the form configInflater(entry)(masterCellConfig)
 * @param {Object} entry from the data source
 * @param {Object} masterCellConfig master cell configuration coming from zapp
 * @param {String} masterCellConfig.type of the component
 * @param {Object} masterCellConfig.style default style object for the component
 * @param {?Object} masterCellConfig.data func/args/propName that indicates how to extract
 * the data from the entry and to which prop it should be injected after manipulation
 * @param {?[Object]} element.elements Optional array of nested elements to render within the current node
 * @returns {Object} inflated configuration, ready to be rendered by the master cell's element mapper
 */

export function configInflater(
  entry: any,
  {
    type,
    style,
    additionalProps = {},
    data = [],
    elements,
  }: {
    type: any;
    style: any;
    additionalProps?: Record<string, any>;
    data?: Array<{ propName: string; func: Function; args: any[] }>;
    elements?: any[];
  }
) {
  const props = data.reduce(
    (acc, curr) => {
      acc[curr.propName] = retrieveData(entry, curr.func, curr.args);

      return acc;
    },
    { ...additionalProps }
  );

  let adjustedElements;

  if (Array.isArray(elements)) {
    adjustedElements = elements.map((element) =>
      configInflater(entry, element as any)
    );
  }

  return {
    type,
    style: resolveColor(entry, style),
    props,
    elements: adjustedElements,
  };
}

/**
 * Return the proper view tree according to entry's content type and UI state.
 * Falls back to "default" content type or state.
 * @param {Object} entry  data source feed entry
 * @param {"selected" | "focused" | ...} state of the current cell
 * @param {Object} elements  master cell configuration
 */

function resolveElementsNode(entry, state, elements) {
  const contentType = entry?.type?.value;

  const contentTypeElement =
    elements.content_types[contentType] || elements.content_types.default;

  return contentTypeElement.states[state] || contentTypeElement.states.default;
}

export function defaultDataAdapter(elements) {
  return function elementsBuilder({ entry, state = "default" }) {
    return resolveElementsNode(entry, state, elements).map((element) =>
      configInflater(entry, element)
    );
  };
}
