import { memo, PropsWithChildren } from 'react';
import { Route as AppRoute, RouteProps as ReactRouterProps, Routes } from 'react-router-dom';

/**
 * Generic type for a parameter in a route
 * @example
 * ```ts
 * interface RouteParams {
 *   userId: RouteParamId
 * }
 * ```
 */
export type RouteParamId = string | number;

/**
 * Type wrapper that combines component props and route params to create an object of
 * both the component props and all Router props with params set internally.
 * @example
 * ```jsx
 * interface RouteParams {
 *   userId: string;
 * }
 *
 * interface RoutedComponentProps {
 *   name: string;
 * }
 *
 * export const RoutedComponent = (props: PropsWithChildren<ComponentOwnProps<RoutedComponentProps, RouteParams>>) => (
 *   <div>
 *     The UserID is: {props.match.params.userId}
 *     The name is: {props.name}
 *   </div>
 * )
 * ```
 */
export type ComponentOwnProps<
  CP extends Record<PropertyKey, unknown>,
  Params extends { [K in keyof Params]?: string } = Record<PropertyKey, string>
> = CP;

export interface Route {
  path: ReactRouterProps['path'];
  component: ReactRouterProps['element'];
  reactRouterProps?: Omit<ReactRouterProps, 'element' | 'path'>;
  /** Data-qa tag to apply to the route */
  'data-qa'?: string;
}

export interface RouterProps extends ReactRouterProps {
  /** The component and route that should be rendered by default (root path and fallback) */
  defaultComponent: Route;
  /** Additional routes to render other than the default component */
  otherComponents?: Route[];

  /** Data-qa tag to apply to the default component route */
  'data-qa'?: string;
}

/**
 * Constructs a Router with provided routes and components
 * @param params Includes list of paths and components as well as the option to pass data-qa and additional props
 * @example
 * ```jsx
 *  <div>
 *    <Router
 *      defaultComponent={{ path: '/', component: () => <div>TEST</div> }}
 *      data-qa='TestRouter'
 *      otherComponents={[
 *        { path: '/app', component: () => <div>APP</div>, 'data-qa': 'AppRoute' },
 *        { path: '/contact', component: () => <Contact />, 'data-qa': 'ContactRoute' }
 *      ]}
 *    />
 *  </div>
 * ```
 */
export const Router = memo(({ defaultComponent, otherComponents, ...props }: PropsWithChildren<RouterProps>) => {
  return (
    <Routes>
      <AppRoute path={defaultComponent.path || ''} element={defaultComponent.component} {...props} />
      {otherComponents?.map((child, index) => (
        <AppRoute key={index} path={child.path || ''} element={child.component} {...child.reactRouterProps} />
      ))}
      <AppRoute element={defaultComponent.component} {...props} />
    </Routes>
  );
});
