import * as R from "ramda";
import * as React from "react";
import { v4 as uuid } from "uuid";

import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
import { useScreenState } from "@applicaster/zapp-react-native-utils/screenState";
import { defaultComponents } from "./DefaultComponents";
import { defaultDataAdapter } from "./dataAdapter";
import { elementMapper, mapElementWithKey } from "./elementMapper";
import { getEntryState, hasFocusableInsideBuilder } from "./utils";

type Props = {
  item: ZappEntry;
  state: null | "selected" | "focused";
};

const defaultContainerStyle = { flex: 1 };

/**
 * Constructs a MasterCell with the provided options
 * @param {Object} options
 * @param {Object} options.components maps of React components to use when rendering the master cell
 * @param {Object} options.cellConfiguration master cell config coming from Zapp
 * @param {Function} options.dataAdapter optional dataAdapter to use instead of the default one.
 * See dataAdapter.js for more info
 * @param {containerStyles} options.containerStyle style to apply at the top level of the master cell
 * @returns {Function} Functional React Component to render the masterCell with the provided options
 */

type MasterCellBuilderOptions = {
  components?: typeof defaultComponents;
  dataAdapter?: typeof defaultDataAdapter;
  cellConfiguration: any;
  containerStyle: any;
  cellOptions?: {
    isRTL?: boolean;
    generateId?: () => string;
    skipButtons?: boolean;
  };
};

export function masterCellBuilder({
  components = defaultComponents,
  cellConfiguration,
  dataAdapter = defaultDataAdapter,
  containerStyle = defaultContainerStyle,
  cellOptions = {},
}: MasterCellBuilderOptions) {
  const { generateId = uuid, skipButtons = false } = cellOptions;

  const componentsMap = R.merge(defaultComponents, components);
  const { View } = componentsMap;
  const elementsBuilder = dataAdapter(cellConfiguration);

  // This is very expensive operation
  // Keep calls to minimum
  const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);

  /**
   * Functional React Component to use to render the master cell
   * @param {Object} props
   * @param {Object} props.item item to be rendered. an item is the result of the master cell configuration,
   * inflated with the data coming from the data source
   * @param {"selected" | "focused" | null} props.state state of the component
   */
  function MasterCell({ item, state, ...otherProps }: Props) {
    const { screenData } = useRoute();

    const screenId =
      screenData && "targetScreen" in screenData
        ? screenData?.targetScreen?.id
        : screenData?.id;

    const { screen } = useScreenState(screenId);

    const selectedState = screen?.selectedEntry;

    const entryIsSelected = React.useMemo(
      () => !!(selectedState?.id && selectedState?.id === item?.id),
      [selectedState?.id, item?.id]
    );

    const elements = React.useMemo(
      () =>
        elementsBuilder({
          entry: item,
          state: getEntryState(state, entryIsSelected),
        }),
      [state, item?.id, entryIsSelected] // Assuming that item won't mutate
    );

    const wrapperRef = React.useRef(null);

    const cellUUID = React.useMemo<string>(generateId, [
      generateId,
      screenId,
      item?.id,
    ]);

    const remapElements = mapElementWithKey(
      elementMapper(componentsMap, {
        ...otherProps,
        wrapperRef,
        cellUUID,
        skipButtons,
      })
    );

    return (
      <View style={containerStyle} ref={wrapperRef}>
        {elements.map(remapElements)}
      </View>
    );
  }

  const _MasterCell = React.memo(MasterCell);

  // @ts-ignore
  _MasterCell.hasFocusableInside = hasFocusableInside;

  return _MasterCell;
}
