import getChildEventSubscriber from './getChildEventSubscriber';
import getChildRouter from './getChildRouter';
import getNavigationActionCreators from './routers/getNavigationActionCreators';
import getChildrenNavigationCache from './getChildrenNavigationCache';

const createParamGetter = route => (paramName, defaultValue) => {
  const params = route.params;

  if (params && paramName in params) {
    return params[paramName];
  }

  return defaultValue;
};

function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
  const children = getChildrenNavigationCache(navigation);
  const childRoute = navigation.state.routes.find(r => r.key === childKey);

  if (!childRoute) {
    return null;
  }

  if (children[childKey] && children[childKey].state === childRoute) {
    return children[childKey];
  }

  const childRouter = getChildRouter(navigation.router, childRoute.routeName);

  // If the route has children, we'll use this to pass in to the action creators
  // for the childRouter so that any action that depends on the active route will
  // behave as expected. We don't explicitly require that routers implement routes
  // and index properties, but if we did then we would put an invariant here to
  // ensure that a focusedGrandChildRoute exists if childRouter is defined.
  const focusedGrandChildRoute =
    childRoute.routes && typeof childRoute.index === 'number'
      ? childRoute.routes[childRoute.index]
      : null;

  const actionCreators = {
    ...navigation.actions,
    ...navigation.router.getActionCreators(childRoute, navigation.state.key),
    ...(childRouter
      ? childRouter.getActionCreators(focusedGrandChildRoute, childRoute.key)
      : {}),
    ...getNavigationActionCreators(childRoute),
  };

  const actionHelpers = {};
  Object.keys(actionCreators).forEach(actionName => {
    actionHelpers[actionName] = (...args) => {
      const actionCreator = actionCreators[actionName];
      const action = actionCreator(...args);
      return navigation.dispatch(action);
    };
  });

  let isFirstRouteInParent = true;

  const parentNavigation = getCurrentParentNavigation();

  if (parentNavigation) {
    isFirstRouteInParent =
      parentNavigation.state.routes.indexOf(childRoute) === 0;
  }

  if (
    children[childKey] &&
    children[childKey].isFirstRouteInParent() === isFirstRouteInParent
  ) {
    children[childKey] = {
      ...children[childKey],
      ...actionHelpers,
      state: childRoute,
      router: childRouter,
      actions: actionCreators,
      getParam: createParamGetter(childRoute),
    };
    return children[childKey];
  } else {
    const childSubscriber = getChildEventSubscriber(
      navigation.addListener,
      childKey
    );

    children[childKey] = {
      ...actionHelpers,

      state: childRoute,
      router: childRouter,
      actions: actionCreators,
      getParam: createParamGetter(childRoute),

      getChildNavigation: grandChildKey =>
        getChildNavigation(children[childKey], grandChildKey, () => {
          const nav = getCurrentParentNavigation();
          return nav && nav.getChildNavigation(childKey);
        }),

      isFocused: () => {
        const currentNavigation = getCurrentParentNavigation();
        if (!currentNavigation) {
          return false;
        }
        const { routes, index } = currentNavigation.state;
        if (!currentNavigation.isFocused()) {
          return false;
        }
        if (routes[index].key === childKey) {
          return true;
        }
        return false;
      },
      isFirstRouteInParent: () => isFirstRouteInParent,
      dispatch: navigation.dispatch,
      getScreenProps: navigation.getScreenProps,
      dangerouslyGetParent: getCurrentParentNavigation,
      addListener: childSubscriber.addListener,
      emit: childSubscriber.emit,
    };
    return children[childKey];
  }
}

export default getChildNavigation;