import React, {
  Children,
  cloneElement,
  DetailedReactHTMLElement,
  isValidElement,
} from 'react';
import type { TPaymentMethodGAppFlow } from './module.type';
import useViewModel from './useViewModel';

const PaymentMethodGAppFlow = ({
  dataLoad,
  dataIn,
  dataOut,
  children,
}: TPaymentMethodGAppFlow) => {
  const model: any = useViewModel({ dataLoad, dataIn, dataOut });
  const Stack = dataIn?.StackNavigator;

  const isActiveNavigation = Stack ? Stack : null;

  // list of screen's
  const screens = dataIn?.screens;

  /**
   * assigning dataLoad, dataIn, dataOut per mini-apps
   * @param child any - child of GApp wrapper
   * @param index number - index of GApp child
   * @returns ReactElement - react element(mini-app) that has a modified dataIn, dataLoad, and dataOut
   */
  const modifyChildrens: any = (
    child: DetailedReactHTMLElement<any, HTMLElement>,
    index: number
  ) => {
    if (isValidElement(child)) {
      // mini-app index
      const mAppIndex = index || 0;

      const screen = screens?.[mAppIndex];

      // mini-app key based on mini-app-key or screen-key
      const mAppScreenKey: any = child?.key || screen?.key;

      // this will be the props of your children
      let mAppProps = child.props;

      const dataInProps = mAppProps?.dataLoad;

      // if child element has dataLoad props
      if (dataInProps !== undefined) {
        // data of GApp internal dataLoad (view model)
        const internalDataLoad = model[mAppScreenKey]?.dataLoad;
        // data of screen dataLoad
        const screenDataLoad = screen?.dataLoad;

        mAppProps = {
          ...mAppProps,
          dataLoad: Array.isArray(internalDataLoad)
            ? screenDataLoad || internalDataLoad || dataInProps
            : {
                ...mAppProps?.dataLoad,
                ...internalDataLoad,
                ...screenDataLoad,
              },
        };
      }

      // if child element has dataIn props
      if (mAppProps?.dataIn !== undefined) {
        // data of GApp internal dataIn (view model)
        const internalDataIn = model[mAppScreenKey]?.dataIn;

        // data of screen dataIn
        const screenDataIn = screen?.dataIn || {};

        mAppProps = {
          ...mAppProps,
          dataIn: {
            ...mAppProps?.dataIn,
            ...internalDataIn,
            ...screenDataIn,
          },
        };
      }

      // if child element has dataOut props
      if (mAppProps?.dataOut !== undefined) {
        // dataOut of GApp internal dataOut (view model)
        const internalDataOut = model[mAppScreenKey]?.dataOut;

        mAppProps = {
          ...mAppProps,
          dataOut: screen?.dataOut || internalDataOut || mAppProps.dataOut,
        };
      }

      if (isActiveNavigation) {
        return modifyChildElement({ child, mAppProps, screen });
      }

      return cloneElement(child, mAppProps);
    } else {
      return child;
    }
  };

  const modifyChildElement: any = ({ child, mAppProps, screen }: any) => {
    return (
      <Stack.Screen name={screen.stackName} options={screen.stackOptions}>
        {(stackProps: any) =>
          cloneElement(child, Object.assign(stackProps, mAppProps))
        }
      </Stack.Screen>
    );
  };

  const modifyAllChildrens = () => {
    if (isActiveNavigation) {
      return (
        <Stack.Navigator initialRouteName={dataIn.initialRouteName}>
          {Children.map(children, modifyChildrens)}
        </Stack.Navigator>
      );
    } else {
      return Children.map(children, modifyChildrens);
    }
  };

  return modifyAllChildrens();
};

export default PaymentMethodGAppFlow;
