import baseLogger from './logger.js';

function parseFormatStr(formatStr) {
  let formatName = formatStr.toLowerCase().trim();
  let formatOptions = {};
  if (formatStr.indexOf('(') > -1) {
    const p = formatStr.split('(');
    formatName = p[0].toLowerCase().trim();

    const optStr = p[1].substring(0, p[1].length - 1);

    // extra for currency
    if (formatName === 'currency' && optStr.indexOf(':') < 0) {
      if (!formatOptions.currency) formatOptions.currency = optStr.trim();
    } else if (formatName === 'relativetime' && optStr.indexOf(':') < 0) {
      if (!formatOptions.range) formatOptions.range = optStr.trim();
    } else {
      const opts = optStr.split(';');

      opts.forEach((opt) => {
        if (!opt) return;
        const [key, ...rest] = opt.split(':');
        const val = rest.join(':');

        if (!formatOptions[key.trim()]) formatOptions[key.trim()] = val.trim();
        if (val.trim() === 'false') formatOptions[key.trim()] = false;
        if (val.trim() === 'true') formatOptions[key.trim()] = true;
        if (!isNaN(val.trim())) formatOptions[key.trim()] = parseInt(val.trim(), 10);
      });
    }
  }

  return {
    formatName,
    formatOptions,
  };
}

class Formatter {
  constructor(options = {}) {
    this.logger = baseLogger.create('formatter');

    this.options = options;
    this.formats = {
      number: (val, lng, options) => {
        return new Intl.NumberFormat(lng, options).format(val);
      },
      currency: (val, lng, options) => {
        return new Intl.NumberFormat(lng, { ...options, style: 'currency' }).format(val);
      },
      datetime: (val, lng, options) => {
        return new Intl.DateTimeFormat(lng, { ...options }).format(val);
      },
      relativetime: (val, lng, options) => {
        return new Intl.RelativeTimeFormat(lng, { ...options }).format(val, options.range || 'day');
      },
      list: (val, lng, options) => {
        return new Intl.ListFormat(lng, { ...options }).format(val);
      },
    };
    this.init(options);
  }

  /* eslint no-param-reassign: 0 */
  init(services, options = { interpolation: {} }) {
    const iOpts = options.interpolation;

    this.formatSeparator = iOpts.formatSeparator
      ? iOpts.formatSeparator
      : iOpts.formatSeparator || ',';
  }

  add(name, fc) {
    this.formats[name.toLowerCase().trim()] = fc;
  }

  format(value, format, lng, options) {
    const formats = format.split(this.formatSeparator);

    const result = formats.reduce((mem, f) => {
      const { formatName, formatOptions } = parseFormatStr(f);

      if (this.formats[formatName]) {
        let formatted = mem;
        try {
          // options passed explicit for that formatted value
          const valOptions =
            (options && options.formatParams && options.formatParams[options.interpolationkey]) ||
            {};

          // language
          const l = valOptions.locale || valOptions.lng || options.locale || options.lng || lng;

          formatted = this.formats[formatName](mem, l, {
            ...formatOptions,
            ...options,
            ...valOptions,
          });
        } catch (error) {
          this.logger.warn(error);
        }
        return formatted;
      } else {
        this.logger.warn(`there was no format function for ${formatName}`);
      }
      return mem;
    }, value);

    return result;
  }
}

export default Formatter;
