import React from 'react';
import { ParsedUrlQuery } from 'querystring';
import { IntlShape, IntlProvider, MessageDescriptor } from 'react-intl';

import localeConfig from './config.json';

export const { locales, defaultLocale } = localeConfig;

type IntlPaths = Array<{ params: { locale: string } }>;
interface IntlProps {
  locale: string;
  messages: Record<string, string>;
}

/**
 * Gets the paths of every locale that we currently support (i.e. locales that
 * we have translations for). This enables us to statically pre-render all of
 * our support locales.
 * @see {@link https://bit.ly/2L1kOFm}
 */
export function getIntlPaths(): IntlPaths {
  return locales.map((locale: string) => ({ params: { locale } }));
}

/**
 * Dynamically imports the requested locale's static translations/messages file
 * and `full-icu` support (i.e. for `Date` stringification) for the given
 * locale.
 *
 * This function is called with the `params` generated by `getIntlPaths()` and
 * ensures that our SSG (Static Site Generation) pages only contain the bare
 * minimum `Intl' support for their target locale..
 * @see {@link https://bit.ly/2SAqwCe}
 */
export async function getIntlProps(context: {
  params?: ParsedUrlQuery;
}): Promise<IntlProps> {
  const locale: string =
    ((context.params || {}).locale as string) || defaultLocale;
  const messages: Record<string, string> = Object.fromEntries(
    (
      await import(`./locales/${locale}.json`)
    ).default.map((msg: MessageDescriptor) => [msg.id, msg.defaultMessage])
  );
  return { locale, messages };
}

/**
 * Adds a HOC (Higher Order Component) to the given component to provide it with
 * the `Intl` information it needs (via `react-intl`'s `IntlProvider`).
 * @see {@link https://bit.ly/3c5vXRg}
 *
 * Types were made easy to define thanks to the following blog post.
 * @see {@link https://bit.ly/3b7ZuZb}
 */
export function withIntl<P extends Record<string, any>>(
  Component: React.ComponentType<P>
): React.FunctionComponent<P & IntlProps> {
  function WithIntl({ locale, messages, ...props }: IntlProps): JSX.Element {
    return (
      <IntlProvider
        locale={locale}
        messages={messages}
        defaultLocale={defaultLocale}
        onError={(error) => console.log('[ERROR] Setting up intl:', error)}
      >
        <Component {...(props as P)} />
      </IntlProvider>
    );
  }

  return WithIntl;
}

/**
 * This is the type of an i18n helper function commonly defined within
 * components to make using `intl.formatMessage` easier (note that this function
 * has to be defined within the React tree using a valid `IntlShape` object
 * which is why it can't just be defined here).
 */
export type IntlHelper = IntlShape['formatMessage'];
