import { MissingMessageHandler } from './MissingMessageHandler';
import _ from 'lodash';
import { I18nProvider, Locale, TranslationMessages } from 'ra-core';
import { default as polyglotI18nProvider } from 'ra-i18n-polyglot';

type IMessage = {
  id: string;
  code: string;
  lang: string;
  text: string;
};
type IDictionary = { [key: string]: string };
type ITranslations = { [key: string]: IDictionary };
type ICreateI18nProviderData = {
  apiUrl: string;
  defaultLocale: string;
  allowMissing?: boolean;
  createMissing?: boolean;
  customProvider?: Promise<I18nProvider>;
};
type ICreateJsonI18nProviderData = {
  path: string;
  locales: Locale[];
  defaultLocale: string;
};
type IPolyglotOptions = {
  phrases?: IDictionary;
  locale?: string;
  allowMissing?: boolean;
  onMissingKey?: (key: string, options: IPolyglotOptions, locale: string) => string;
  interpolation?: {
    prefix?: string;
    suffix?: string;
  };
};

function createI18nProvider(data: ICreateI18nProviderData): Promise<any> {
  const { apiUrl, defaultLocale, allowMissing = false, createMissing = false } = data;
  const defaultLanguages: Array<Locale> = [{ locale: defaultLocale, name: defaultLocale }];
  const headers = new Headers({ Accept: 'application/json', 'Content-Type': 'application/json' });
  const missingMessageHandler = new MissingMessageHandler({ apiUrl: apiUrl });
  function getLanguages(): Promise<Locale> {
    return fetch(`${apiUrl}/i18n/languages`, { headers: headers, method: 'get' })
      .then((response) => response.json())
      .catch(() => {
        return Promise.resolve(defaultLanguages);
      });
  }
  function getMessages(): Promise<ITranslations> {
    return fetch(`${apiUrl}/i18n/messages`, { headers: headers, method: 'get' })
      .then((response) => response.json())
      .then((response: Array<IMessage>): ITranslations => {
        return response.reduce(
          (messages: ITranslations, message: IMessage) => ({
            ...messages,
            [message.lang]: {
              ...(messages[message.lang] || {}),
              [message.code]: message.text
            }
          }),
          {}
        );
      });
  }
  function onMissingKey(key: string, _options: IPolyglotOptions, locale: string): string {
    if (!allowMissing) {
      //eslint-disable-next-line
      console.error(`Warning: Missing translation for key: "${key}"`);
    }
    if (createMissing) {
      if (!_.isString(key)) {
        //eslint-disable-next-line
        console.error('[createI18nProvider] onMissingKey: provided key is not a string');
      } else if (key.match(new RegExp(/[\s[\]]/, 'gm'))) {
        //eslint-disable-next-line
        console.error(`[createI18nProvider] onMissingKey: illegal character in key ${key}`);
      } else {
        missingMessageHandler.handleMessage({ code: key, lang: locale });
      }
    }
    return key;
  }

  return Promise.all([getLanguages(), getMessages()]).then((responses) => {
    const [languages, messages] = responses;
    function getMessages(locale: string): TranslationMessages {
      //@ts-ignore
      return _.get(messages, locale, {});
    }
    return polyglotI18nProvider(getMessages, defaultLocale, languages, {
      allowMissing: allowMissing || createMissing,
      onMissingKey: onMissingKey
    });
  });
}

/**
 * Create an I18nProvider that fetches translations from JSON files.
 *
 * @param data {ICreateI18nProviderData} - The data to create the I18nProvider
 * @returns {Promise<I18nProvider>} - The I18nProvider
 */
function createJsonI18nProvider(data: ICreateJsonI18nProviderData): Promise<I18nProvider> {
  const { locales, defaultLocale, path } = data;
  const headers = new Headers({ Accept: 'application/json', 'Content-Type': 'application/json' });

  function getMessages(locale: Locale): Promise<ITranslations> {
    return fetch(`${path}/${locale.locale}.json`, { headers: headers, method: 'get' })
      .then((response) => response.json())
      .then((response: Array<IMessage>): ITranslations => {
        return response.reduce(
          (messages: ITranslations, message: IMessage) => ({
            ...messages,
            [message.lang]: {
              ...(messages[message.lang] || {}),
              [message.code]: message.text
            }
          }),
          {}
        );
      });
  }
  return Promise.all(locales.map(getMessages))
    .then((responses: ITranslations[]) =>
      _.reduce(
        responses,
        (acc, messages) =>
          ({
            ...acc,
            ...messages
          } as ITranslations),
        {}
      )
    )
    .then((messages) => {
      function getMessages(locale: string): TranslationMessages {
        return _.get(messages, locale, {}) as TranslationMessages;
      }
      return polyglotI18nProvider(getMessages, defaultLocale, locales, {
        allowMissing: true
      });
    });
}

export type { ICreateI18nProviderData };
export { createI18nProvider, createJsonI18nProvider };
