import * as React from 'react';
import {IntlConfig, Formatters, IntlFormatters} from '../types';
import {filterProps} from '../utils';
import IntlListFormat, {IntlListFormatOptions} from '@formatjs/intl-listformat';
import {FormatError, ErrorCode} from 'intl-messageformat';
import {ReactIntlError, ReactIntlErrorCode} from '../error';

const LIST_FORMAT_OPTIONS: Array<keyof IntlListFormatOptions> = [
  'localeMatcher',
  'type',
  'style',
];

const now = Date.now();

function generateToken(i: number): string {
  return `${now}_${i}_${now}`;
}

export function formatList(
  {locale, onError}: Pick<IntlConfig, 'locale' | 'onError'>,
  getListFormat: Formatters['getListFormat'],
  values: Array<string>,
  options: Parameters<IntlFormatters['formatList']>[1]
): string;
export function formatList(
  {locale, onError}: Pick<IntlConfig, 'locale' | 'onError'>,
  getListFormat: Formatters['getListFormat'],
  values: Parameters<IntlFormatters['formatList']>[0],
  options: Parameters<IntlFormatters['formatList']>[1] = {}
): React.ReactNode {
  const ListFormat: typeof IntlListFormat = (Intl as any).ListFormat;
  if (!ListFormat) {
    onError(
      new FormatError(
        `Intl.ListFormat is not available in this environment.
Try polyfilling it using "@formatjs/intl-listformat"
`,
        ErrorCode.MISSING_INTL_API
      )
    );
  }
  const filteredOptions = filterProps(options, LIST_FORMAT_OPTIONS);

  try {
    const richValues: Record<string, React.ReactNode> = {};
    const serializedValues = values.map((v, i) => {
      if (typeof v === 'object') {
        const id = generateToken(i);
        richValues[id] = v;
        return id;
      }
      return String(v);
    });
    if (!Object.keys(richValues).length) {
      return getListFormat(locale, filteredOptions).format(serializedValues);
    }
    const parts = getListFormat(locale, filteredOptions).formatToParts(
      serializedValues
    );
    return parts.reduce((all: Array<string | React.ReactNode>, el) => {
      const val = el.value;
      if (richValues[val]) {
        all.push(richValues[val]);
      } else if (typeof all[all.length - 1] === 'string') {
        all[all.length - 1] += val;
      } else {
        all.push(val);
      }
      return all;
    }, []);
  } catch (e) {
    onError(
      new ReactIntlError(
        ReactIntlErrorCode.FORMAT_ERROR,
        'Error formatting list.',
        e
      )
    );
  }

  return values;
}
