import 'server-only';

import { cookies, headers } from 'next/headers';

import type { I18n, Messages } from '@lingui/core';
import { setupI18n } from '@lingui/core';
import { setI18n } from '@lingui/react/server';

import {
  APP_I18N_OPTIONS,
  SUPPORTED_LANGUAGE_CODES,
  isValidLanguageCode,
} from '../../constants/i18n';
import { extractLocaleData } from '../../utils/i18n';
import { remember } from '../../utils/remember';

type SupportedLanguages = (typeof SUPPORTED_LANGUAGE_CODES)[number];

export async function loadCatalog(lang: SupportedLanguages): Promise<{
  [k: string]: Messages;
}> {
  const extension = process.env.NODE_ENV === 'development' ? 'po' : 'js';

  let { messages } = await import(`../../translations/${lang}/web.${extension}`);

  if (extension === 'po') {
    const { messages: commonMessages } = await import(
      `../../translations/${lang}/common.${extension}`
    );

    messages = { ...messages, ...commonMessages };
  }

  return {
    [lang]: messages,
  };
}

const catalogs = Promise.all(SUPPORTED_LANGUAGE_CODES.map(loadCatalog));

// transform array of catalogs into a single object
const allMessages = async () => {
  return await catalogs.then((catalogs) =>
    catalogs.reduce((acc, oneCatalog) => {
      return {
        ...acc,
        ...oneCatalog,
      };
    }, {}),
  );
};

type AllI18nInstances = { [K in SupportedLanguages]: I18n };

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
export const allI18nInstances = remember('i18n.allI18nInstances', async () => {
  const loadedMessages = await allMessages();

  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
  return SUPPORTED_LANGUAGE_CODES.reduce((acc, lang) => {
    const messages = loadedMessages[lang] ?? {};

    const i18n = setupI18n({
      locale: lang,
      messages: { [lang]: messages },
    });

    return { ...acc, [lang]: i18n };
  }, {}) as AllI18nInstances;
});

// eslint-disable-next-line @typescript-eslint/ban-types
export const getI18nInstance = async (lang?: SupportedLanguages | (string & {})) => {
  const instances = await allI18nInstances;

  if (!isValidLanguageCode(lang)) {
    return instances[APP_I18N_OPTIONS.sourceLang];
  }

  return instances[lang] ?? instances[APP_I18N_OPTIONS.sourceLang];
};

/**
 * This needs to be run in all layouts and page server components that require i18n.
 *
 * https://lingui.dev/tutorials/react-rsc#pages-layouts-and-lingui
 */
export const setupI18nSSR = async () => {
  const { lang, locales } = extractLocaleData({
    cookies: cookies(),
    headers: headers(),
  });

  // Get and set a ready-made i18n instance for the given language.
  const i18n = await getI18nInstance(lang);

  // Reactivate the i18n instance with the locale for date and number formatting.
  i18n.activate(lang, locales);

  setI18n(i18n);

  return {
    lang,
    locales,
    i18n,
  };
};
