// This is a port of Google Android `libphonenumber`'s
// `phonenumberutil.js` of December 31th, 2018.
//
// https://github.com/googlei18n/libphonenumber/commits/master/javascript/i18n/phonenumbers/phonenumberutil.js

import matchesEntirely from './helpers/matchesEntirely.js';
import formatNationalNumberUsingFormat from './helpers/formatNationalNumberUsingFormat.js';
import Metadata, { getCountryCallingCode } from './metadata.js';
import getIddPrefix from './helpers/getIddPrefix.js';
import { formatRFC3966 } from './helpers/RFC3966.js';
const DEFAULT_OPTIONS = {
  formatExtension: (formattedNumber, extension, metadata) => `${formattedNumber}${metadata.ext()}${extension}`
};

/**
 * Formats a phone number.
 *
 * format(phoneNumberInstance, 'INTERNATIONAL', { ..., v2: true }, metadata)
 * format(phoneNumberInstance, 'NATIONAL', { ..., v2: true }, metadata)
 *
 * format({ phone: '8005553535', country: 'RU' }, 'INTERNATIONAL', { ... }, metadata)
 * format({ phone: '8005553535', country: 'RU' }, 'NATIONAL', undefined, metadata)
 *
 * @param  {object|PhoneNumber} input — If `options.v2: true` flag is passed, the `input` should be a `PhoneNumber` instance. Otherwise, it should be an object of shape `{ phone: '...', country: '...' }`.
 * @param  {string} format
 * @param  {object} [options]
 * @param  {object} metadataJson
 * @return {string}
 */
export default function formatNumber(input, format, options, metadataJson) {
  // Apply default options.
  if (options) {
    // Using ES6 "rest spread" syntax here didn't work with `babel`/`istanbul`
    // for some weird reason: this line of code would cause the code coverage
    // to show as not 100%. That's because `babel`/`istanbul`, for some weird reason,
    // apparently doesn't know how to properly exclude Babel polyfills from code coverage.
    //
    // options = { ...DEFAULT_OPTIONS, ...options }
    //
    options = merge({}, DEFAULT_OPTIONS, options);
  } else {
    options = DEFAULT_OPTIONS;
  }
  const metadata = new Metadata(metadataJson);

  // Normally, the `input` object is supposed to be a `PhoneNumber` class instance.
  // Also, according to the `PhoneNumber` class source code, `country` can't be "001".
  // It means that normally `input.country` or `input.countryCallingCode` is supposed
  // to be present because either of the two is always required to exist in a `PhoneNumber` instance.
  // This means that realistically, it's gonna step into either the first `if`
  // or the following `else if`, and normally it won't even reach the legacy-compatibility
  // `else return input.phone || ''` part.
  // So normally, it won't ever return an empty string here.
  if (input.country && input.country !== '001') {
    // Validate `input.country`.
    if (!metadata.hasCountry(input.country)) {
      throw new Error(`Unknown country: ${input.country}`);
    }
    metadata.selectNumberingPlan(input.country);
  } else if (input.countryCallingCode) {
    metadata.selectNumberingPlan(input.countryCallingCode);
  } else return input.phone || '';
  const countryCallingCode = metadata.countryCallingCode();
  const nationalNumber = options.v2 ? input.nationalNumber : input.phone;

  // This variable should have been declared inside `case`s
  // but Babel has a bug and it says "duplicate variable declaration".
  let number;
  switch (format) {
    case 'NATIONAL':
      // Normally, the `input` object is supposed to be a `PhoneNumber` class instance,
      // and a `PhoneNumber` class instance is always required to have a `nationalNumber`.
      // This means that the `if (!nationalNumber)` below is just for legacy-compatibility
      // and it normally can't really happen, so normally it won't ever return an empty string here.
      if (!nationalNumber) {
        return '';
      }
      number = formatNationalNumber(nationalNumber, input.carrierCode, 'NATIONAL', metadata, options);
      return addExtension(number, input.ext, metadata, options.formatExtension);
    case 'INTERNATIONAL':
      // Legacy argument support.
      // (`{ country: ..., phone: '' }`)
      if (!nationalNumber) {
        return `+${countryCallingCode}`;
      }
      number = formatNationalNumber(nationalNumber, null, 'INTERNATIONAL', metadata, options);
      number = `+${countryCallingCode} ${number}`;
      return addExtension(number, input.ext, metadata, options.formatExtension);
    case 'E.164':
      // `E.164` doesn't define "phone number extensions".
      return `+${countryCallingCode}${nationalNumber}`;
    case 'RFC3966':
      return formatRFC3966({
        number: `+${countryCallingCode}${nationalNumber}`,
        ext: input.ext
      });

    // For reference, here's Google's IDD formatter:
    // https://github.com/google/libphonenumber/blob/32719cf74e68796788d1ca45abc85dcdc63ba5b9/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java#L1546
    // Not saying that this IDD formatter replicates it 1:1, but it seems to work.
    // Who would even need to format phone numbers in IDD format anyway?
    case 'IDD':
      // If the required `fromCountry` parameter is not passed, it will return `undefined`.
      if (!options.fromCountry) {
        return;
        // throw new Error('`fromCountry` option not passed for IDD-prefixed formatting.')
      }
      const formattedNumber = formatIDD(nationalNumber, input.carrierCode, countryCallingCode, options.fromCountry, metadata);
      // If the country of the phone number doesn't support IDD calling, it will return `undefined`.
      if (!formattedNumber) {
        return;
      }
      return addExtension(formattedNumber, input.ext, metadata, options.formatExtension);
    default:
      throw new Error(`Unknown "format" argument passed to "formatNumber()": "${format}"`);
  }
}
function formatNationalNumber(number, carrierCode, formatAs, metadata, options) {
  const format = chooseFormatForNumber(metadata.formats(), number);
  if (!format) {
    return number;
  }
  return formatNationalNumberUsingFormat(number, format, {
    useInternationalFormat: formatAs === 'INTERNATIONAL',
    withNationalPrefix: format.nationalPrefixIsOptionalWhenFormattingInNationalFormat() && options && options.nationalPrefix === false ? false : true,
    carrierCode,
    metadata
  });
}
export function chooseFormatForNumber(availableFormats, nationalNumber) {
  // Using a `for ... of` loop here didn't work with `babel`/`istanbul`:
  // for some weird reason, it showed code coverage less than 100%.
  // That's because `babel`/`istanbul`, for some weird reason,
  // apparently doesn't know how to properly exclude Babel polyfills from code coverage.
  //
  // for (const format of availableFormats) { ... }
  //
  return pickFirstMatchingElement(availableFormats, format => {
    // Validate leading digits.
    // The test case for "else path" could be found by searching for
    // "format.leadingDigitsPatterns().length === 0".
    if (format.leadingDigitsPatterns().length > 0) {
      // The last leading_digits_pattern is used here, as it is the most detailed
      const lastLeadingDigitsPattern = format.leadingDigitsPatterns()[format.leadingDigitsPatterns().length - 1];
      // If leading digits don't match then move on to the next phone number format
      if (nationalNumber.search(lastLeadingDigitsPattern) !== 0) {
        return false;
      }
    }
    // Check that the national number matches the phone number format regular expression
    return matchesEntirely(nationalNumber, format.pattern());
  });
}
function addExtension(formattedNumber, ext, metadata, formatExtension) {
  return ext ? formatExtension(formattedNumber, ext, metadata) : formattedNumber;
}
function formatIDD(nationalNumber, carrierCode, countryCallingCode, fromCountry, metadata) {
  const fromCountryCallingCode = getCountryCallingCode(fromCountry, metadata.metadata);
  // When calling within the same country calling code.
  if (fromCountryCallingCode === countryCallingCode) {
    const formattedNumber = formatNationalNumber(nationalNumber, carrierCode, 'NATIONAL', metadata);
    // For NANPA regions, return the national format for these regions
    // but prefix it with the country calling code.
    if (countryCallingCode === '1') {
      return countryCallingCode + ' ' + formattedNumber;
    }
    // If regions share a country calling code, the country calling code need
    // not be dialled. This also applies when dialling within a region, so this
    // if clause covers both these cases. Technically this is the case for
    // dialling from La Reunion to other overseas departments of France (French
    // Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover
    // this edge case for now and for those cases return the version including
    // country calling code. Details here:
    // http://www.petitfute.com/voyage/225-info-pratiques-reunion
    //
    return formattedNumber;
  }
  const iddPrefix = getIddPrefix(fromCountry, undefined, metadata.metadata);
  if (iddPrefix) {
    return `${iddPrefix} ${countryCallingCode} ${formatNationalNumber(nationalNumber, null, 'INTERNATIONAL', metadata)}`;
  }
}
function merge(...objects) {
  let i = 1;
  while (i < objects.length) {
    if (objects[i]) {
      for (const key in objects[i]) {
        objects[0][key] = objects[i][key];
      }
    }
    i++;
  }
  return objects[0];
}
function pickFirstMatchingElement(elements, testFunction) {
  let i = 0;
  while (i < elements.length) {
    if (testFunction(elements[i])) {
      return elements[i];
    }
    i++;
  }
}
//# sourceMappingURL=format.js.map