"use strict";

import * as React from 'react';
import { isValidElementType } from 'react-is';
import { useRoute } from "./useRoute.js";

/**
 * Flatten a type to remove all type alias names, unions etc.
 * This will show a plain object when hovering over the type.
 */

/**
 * keyof T doesn't work for union types. We can use distributive conditional types instead.
 * https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
 */

/**
 * We get a union type when using keyof, but we want an intersection instead.
 * https://stackoverflow.com/a/50375286/1665026
 */

/**
 * Props for a screen component which is rendered by a static navigator.
 * Takes the route params as a generic argument.
 */

/**
 * Infer the param list from the static navigation config.
 */
import { jsx as _jsx } from "react/jsx-runtime";
const MemoizedScreen = /*#__PURE__*/React.memo(({
  component
}) => {
  const route = useRoute();
  const children = /*#__PURE__*/React.createElement(component, {
    route
  });
  return children;
});
MemoizedScreen.displayName = 'Memo(Screen)';
const getItemsFromScreens = (Screen, screens) => {
  return Object.entries(screens).map(([name, item]) => {
    let component;
    let props = {};
    let useIf;
    let isNavigator = false;
    if ('screen' in item) {
      const {
        screen,
        if: _if,
        ...rest
      } = item;
      useIf = _if;
      props = rest;
      if (isValidElementType(screen)) {
        component = screen;
      } else if ('config' in screen) {
        isNavigator = true;
        component = createComponentForStaticNavigation(screen, `${name}Navigator`);
      }
    } else if (isValidElementType(item)) {
      component = item;
    } else if ('config' in item) {
      isNavigator = true;
      component = createComponentForStaticNavigation(item, `${name}Navigator`);
    }
    if (component == null) {
      throw new Error(`Couldn't find a 'screen' property for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`);
    }
    const element = isNavigator ? (/*#__PURE__*/React.createElement(component, {})) : /*#__PURE__*/_jsx(MemoizedScreen, {
      component: component
    });
    return () => {
      const shouldRender = useIf == null || useIf();
      if (!shouldRender) {
        return null;
      }
      return /*#__PURE__*/_jsx(Screen, {
        name: name,
        ...props,
        children: () => element
      }, name);
    };
  });
};

/**
 * Create a component that renders a navigator based on the static configuration.
 *
 * @param tree Static navigation config.
 * @param displayName Name of the component to be displayed in React DevTools.
 * @returns A component which renders the navigator.
 */
export function createComponentForStaticNavigation(tree, displayName) {
  const {
    Navigator,
    Group,
    Screen,
    config
  } = tree;
  const {
    screens,
    groups,
    ...rest
  } = config;
  if (screens == null && groups == null) {
    throw new Error("Couldn't find a 'screens' or 'groups' property. Make sure to define your screens under a 'screens' property in the configuration.");
  }
  const items = [];

  // Loop through the config to find screens and groups
  // So we add the screens and groups in the same order as they are defined
  for (const key in config) {
    if (key === 'screens' && screens) {
      items.push(...getItemsFromScreens(Screen, screens));
    }
    if (key === 'groups' && groups) {
      items.push(...Object.entries(groups).map(([key, {
        if: useIf,
        ...group
      }]) => {
        const groupItems = getItemsFromScreens(Screen, group.screens);
        return () => {
          // Call unconditionally since screen configs may contain `useIf` hooks
          const children = groupItems.map(item => item());
          const shouldRender = useIf == null || useIf();
          if (!shouldRender) {
            return null;
          }
          return /*#__PURE__*/_jsx(Group, {
            navigationKey: key,
            ...group,
            children: children
          }, key);
        };
      }));
    }
  }
  const NavigatorComponent = () => {
    const children = items.map(item => item());
    return /*#__PURE__*/_jsx(Navigator, {
      ...rest,
      children: children
    });
  };
  NavigatorComponent.displayName = displayName;
  return NavigatorComponent;
}
/**
 * Create a path config object from a static navigation config for deep linking.
 *
 * @param tree Static navigation config.
 * @param options Additional options from `linking.config`.
 * @param auto Whether to automatically generate paths for leaf screens.
 * @returns Path config object to use in linking config.
 *
 * @example
 * ```js
 * const config = {
 *   screens: {
 *     Home: {
 *       screens: createPathConfigForStaticNavigation(HomeTabs),
 *     },
 *   },
 * };
 * ```
 */
export function createPathConfigForStaticNavigation(tree, options, auto) {
  let initialScreenHasPath = false;
  let initialScreenConfig;
  const createPathConfigForTree = (t, o, skipInitialDetection) => {
    const createPathConfigForScreens = (screens, initialRouteName) => {
      return Object.fromEntries(Object.entries(screens)
      // Re-order to move the initial route to the front
      // This way we can detect the initial route correctly
      .sort(([a], [b]) => {
        if (a === initialRouteName) {
          return -1;
        }
        if (b === initialRouteName) {
          return 1;
        }
        return 0;
      }).map(([key, item]) => {
        const screenConfig = {};
        if ('linking' in item) {
          if (typeof item.linking === 'string') {
            screenConfig.path = item.linking;
          } else {
            Object.assign(screenConfig, item.linking);
          }
          if (typeof screenConfig.path === 'string') {
            screenConfig.path = screenConfig.path.replace(/^\//, '') // Remove extra leading slash
            .replace(/\/$/, ''); // Remove extra trailing slash
          }
        }
        let screens;
        const skipInitialDetectionInChild = skipInitialDetection || screenConfig.path != null && screenConfig.path !== '';
        if ('config' in item) {
          screens = createPathConfigForTree(item, undefined, skipInitialDetectionInChild);
        } else if ('screen' in item && 'config' in item.screen && (item.screen.config.screens || item.screen.config.groups)) {
          screens = createPathConfigForTree(item.screen, undefined, skipInitialDetectionInChild);
        }
        if (screens) {
          screenConfig.screens = screens;
        }
        if (auto && !screenConfig.screens &&
        // Skip generating path for screens that specify linking config as `undefined` or `null` explicitly
        !('linking' in item && item.linking == null)) {
          if (screenConfig.path != null) {
            if (!skipInitialDetection) {
              if (key === initialRouteName && screenConfig.path != null) {
                initialScreenHasPath = true;
              } else if (screenConfig.path === '') {
                // We encounter a leaf screen with empty path,
                // Clear the initial screen config as it's not needed anymore
                initialScreenConfig = undefined;
              }
            }
          } else {
            if (!skipInitialDetection && initialScreenConfig == null) {
              initialScreenConfig = screenConfig;
            }
            screenConfig.path = key.replace(/([A-Z]+)/g, '-$1').replace(/^-/, '').toLowerCase();
          }
        }
        return [key, screenConfig];
      }).filter(([, screen]) => Object.keys(screen).length > 0));
    };
    const screens = {};

    // Loop through the config to find screens and groups
    // So we add the screens and groups in the same order as they are defined
    for (const key in t.config) {
      if (key === 'screens' && t.config.screens) {
        Object.assign(screens, createPathConfigForScreens(t.config.screens, o?.initialRouteName ?? t.config.initialRouteName));
      }
      if (key === 'groups' && t.config.groups) {
        Object.entries(t.config.groups).forEach(([, group]) => {
          Object.assign(screens, createPathConfigForScreens(group.screens, o?.initialRouteName ?? t.config.initialRouteName));
        });
      }
    }
    if (Object.keys(screens).length === 0) {
      return undefined;
    }
    return screens;
  };
  const screens = createPathConfigForTree(tree, options, false);
  if (auto && initialScreenConfig && !initialScreenHasPath) {
    initialScreenConfig.path = '';
  }
  return screens;
}
//# sourceMappingURL=StaticNavigation.js.map