import { parsePhoneNumberFromString, getCountryCallingCode, AsYouType, Metadata } from 'libphonenumber-js/core';
import getInternationalPhoneNumberPrefix from './getInternationalPhoneNumberPrefix';
/**
 * Decides which country should be pre-selected
 * when the phone number input component is first mounted.
 * @param  {object?} phoneNumber - An instance of `PhoneNumber` class.
 * @param  {string?} country - Pre-defined country (two-letter code).
 * @param  {string[]?} countries - A list of countries available.
 * @param  {boolean} includeInternationalOption - Whether "International" country option is available.
 * @param  {object} metadata - `libphonenumber-js` metadata
 * @return {string?}
 */

export function getPreSelectedCountry(_ref) {
  var phoneNumber = _ref.phoneNumber,
      defaultCountry = _ref.defaultCountry,
      countries = _ref.countries,
      required = _ref.required,
      metadata = _ref.metadata;
  var country = defaultCountry; // If can get country from E.164 phone number
  // then it overrides the `country` passed (or not passed).

  if (phoneNumber && phoneNumber.country) {
    // `country` will be left `undefined` in case of non-detection.
    country = phoneNumber.country;
  } // Only pre-select a country if it's in the available `countries` list.


  if (countries && countries.indexOf(country) < 0) {
    country = undefined;
  } // If there will be no "International" option
  // then some `country` must be selected.
  // It will still be the wrong country though.
  // But still country `<select/>` can't be left in a broken state.


  if (!country && required && countries && countries.length > 0) {
    country = countries[0];
  }

  return country;
}
/**
 * Generates a sorted list of country `<select/>` options.
 * @param  {string[]} countries - A list of two-letter ("ISO 3166-1 alpha-2") country codes.
 * @param  {object} labels - Custom country labels. E.g. `{ RU: 'Россия', US: 'США', ... }`.
 * @param  {boolean} includeInternationalOption - Whether should include "International" option at the top of the list.
 * @return {object[]} A list of objects having shape `{ value : string, label : string }`.
 */

export function getCountrySelectOptions(_ref2) {
  var countries = _ref2.countries,
      countryNames = _ref2.countryNames,
      includeInternationalOption = _ref2.includeInternationalOption,
      compareStringsLocales = _ref2.compareStringsLocales,
      _compareStrings = _ref2.compareStrings;

  // Default country name comparator uses `String.localeCompare()`.
  if (!_compareStrings) {
    _compareStrings = compareStrings;
  } // Generates a `<Select/>` option for each country.


  var countrySelectOptions = countries.map(function (country) {
    return {
      value: country,
      // All `locale` country names included in this library
      // include all countries (this is checked at build time).
      // The only case when a country name might be missing
      // is when a developer supplies their own `labels` property.
      // To guard against such cases, a missing country name
      // is substituted by country code.
      label: countryNames[country] || country
    };
  }); // Sort the list of countries alphabetically.

  countrySelectOptions.sort(function (a, b) {
    return _compareStrings(a.label, b.label, compareStringsLocales);
  }); // Add the "International" option to the country list (if suitable)

  if (includeInternationalOption) {
    countrySelectOptions.unshift({
      label: countryNames.ZZ
    });
  }

  return countrySelectOptions;
}
/**
 * Parses a E.164 phone number to an instance of `PhoneNumber` class.
 * @param {string?} value = E.164 phone number.
 * @param  {object} metadata - `libphonenumber-js` metadata
 * @return {object} Object having shape `{ country: string?, countryCallingCode: string, number: string }`. `PhoneNumber`: https://gitlab.com/catamphetamine/libphonenumber-js#phonenumber.
 * @example
 * parsePhoneNumber('+78005553535')
 */

export function parsePhoneNumber(value, metadata) {
  return parsePhoneNumberFromString(value || '', metadata);
}
/**
 * Generates national number digits for a parsed phone.
 * May prepend national prefix.
 * The phone number must be a complete and valid phone number.
 * @param  {object} phoneNumber - An instance of `PhoneNumber` class.
 * @param  {object} metadata - `libphonenumber-js` metadata
 * @return {string}
 * @example
 * getNationalNumberDigits({ country: 'RU', phone: '8005553535' })
 * // returns '88005553535'
 */

export function generateNationalNumberDigits(phoneNumber) {
  return phoneNumber.formatNational().replace(/\D/g, '');
}
/**
 * Migrates parsed `<input/>` `value` for the newly selected `country`.
 * @param {string?} phoneDigits - Phone number digits (and `+`) parsed from phone number `<input/>` (it's not the same as the `value` property).
 * @param {string?} prevCountry - Previously selected country.
 * @param {string?} newCountry - Newly selected country. Can't be same as previously selected country.
 * @param {object} metadata - `libphonenumber-js` metadata.
 * @param {boolean} useNationalFormat - whether should attempt to convert from international to national number for the new country.
 * @return {string?}
 */

export function getPhoneDigitsForNewCountry(phoneDigits, _ref3) {
  var prevCountry = _ref3.prevCountry,
      newCountry = _ref3.newCountry,
      metadata = _ref3.metadata,
      useNationalFormat = _ref3.useNationalFormat;

  // If `parsed_input` is empty
  // then no need to migrate anything.
  if (!phoneDigits) {
    if (useNationalFormat) {
      return '';
    } else {
      // If `phoneDigits` is empty then set `phoneDigits` to
      // `+{getCountryCallingCode(newCountry)}`.
      return getInternationalPhoneNumberPrefix(newCountry, metadata);
    }
  } // If switching to some country.
  // (from "International" or another country)
  // If switching from "International" then `phoneDigits` starts with a `+`.
  // Otherwise it may or may not start with a `+`.


  if (newCountry) {
    // If the phone number was entered in international format
    // then migrate it to the newly selected country.
    // The phone number may be incomplete.
    // The phone number entered not necessarily starts with
    // the previously selected country phone prefix.
    if (phoneDigits[0] === '+') {
      // If the international phone number is for the new country
      // then convert it to local if required.
      if (useNationalFormat) {
        // // If a phone number is being input in international form
        // // and the country can already be derived from it,
        // // and if it is the new country, then format as a national number.
        // const derived_country = getCountryFromPossiblyIncompleteInternationalPhoneNumber(phoneDigits, metadata)
        // if (derived_country === newCountry) {
        // 	return stripCountryCallingCode(phoneDigits, derived_country, metadata)
        // }
        // Actually, the two countries don't necessarily need to match:
        // the condition could be looser here, because several countries
        // might share the same international phone number format
        // (for example, "NANPA" countries like US, Canada, etc).
        // The looser condition would be just "same nternational phone number format"
        // which would mean "same country calling code" in the context of `libphonenumber-js`.
        if (phoneDigits.indexOf('+' + getCountryCallingCode(newCountry, metadata)) === 0) {
          return stripCountryCallingCode(phoneDigits, newCountry, metadata);
        } // Simply discard the previously entered international phone number,
        // because otherwise any "smart" transformation like getting the
        // "national (significant) number" part and then prepending the
        // newly selected country's "country calling code" to it
        // would just be confusing for a user without being actually useful.


        return ''; // // Simply strip the leading `+` character
        // // therefore simply converting all digits into a "local" phone number.
        // // https://github.com/catamphetamine/react-phone-number-input/issues/287
        // return phoneDigits.slice(1)
      }

      if (prevCountry) {
        var newCountryPrefix = getInternationalPhoneNumberPrefix(newCountry, metadata);

        if (phoneDigits.indexOf(newCountryPrefix) === 0) {
          return phoneDigits;
        } else {
          return newCountryPrefix;
        }
      } else {
        var defaultValue = getInternationalPhoneNumberPrefix(newCountry, metadata); // If `phoneDigits`'s country calling code part is the same
        // as for the new `country`, then leave `phoneDigits` as is.

        if (phoneDigits.indexOf(defaultValue) === 0) {
          return phoneDigits;
        } // If `phoneDigits`'s country calling code part is not the same
        // as for the new `country`, then set `phoneDigits` to
        // `+{getCountryCallingCode(newCountry)}`.


        return defaultValue;
      } // // If the international phone number already contains
      // // any country calling code then trim the country calling code part.
      // // (that could also be the newly selected country phone code prefix as well)
      // // `phoneDigits` doesn't neccessarily belong to `prevCountry`.
      // // (e.g. if a user enters an international number
      // //  not belonging to any of the reduced `countries` list).
      // phoneDigits = stripCountryCallingCode(phoneDigits, prevCountry, metadata)
      // // Prepend country calling code prefix
      // // for the newly selected country.
      // return e164(phoneDigits, newCountry, metadata) || `+${getCountryCallingCode(newCountry, metadata)}`

    }
  } // If switching to "International" from a country.
  else {
      // If the phone number was entered in national format.
      if (phoneDigits[0] !== '+') {
        // Format the national phone number as an international one.
        // The phone number entered not necessarily even starts with
        // the previously selected country phone prefix.
        // Even if the phone number belongs to whole another country
        // it will still be parsed into some national phone number.
        return e164(phoneDigits, prevCountry, metadata) || '';
      }
    }

  return phoneDigits;
}
/**
 * Converts phone number digits to a (possibly incomplete) E.164 phone number.
 * @param  {string?} number - A possibly incomplete phone number digits string. Can be a possibly incomplete E.164 phone number.
 * @param  {string?} country
 * @param  {[object} metadata - `libphonenumber-js` metadata.
 * @return {string?}
 */

export function e164(number, country, metadata) {
  if (!number) {
    return;
  } // If the phone number is being input in international format.


  if (number[0] === '+') {
    // If it's just the `+` sign then return nothing.
    if (number === '+') {
      return;
    } // If there are any digits then the `value` is returned as is.


    return number;
  } // For non-international phone numbers
  // an accompanying country code is required.


  if (!country) {
    return;
  }

  var partial_national_significant_number = getNationalSignificantNumberDigits(number, country, metadata);

  if (partial_national_significant_number) {
    return "+".concat(getCountryCallingCode(country, metadata)).concat(partial_national_significant_number);
  }
}
/**
 * Trims phone number digits if they exceed the maximum possible length
 * for a national (significant) number for the country.
 * @param  {string} number - A possibly incomplete phone number digits string. Can be a possibly incomplete E.164 phone number.
 * @param  {string} country
 * @param  {object} metadata - `libphonenumber-js` metadata.
 * @return {string} Can be empty.
 */

export function trimNumber(number, country, metadata) {
  var nationalSignificantNumberPart = getNationalSignificantNumberDigits(number, country, metadata);

  if (nationalSignificantNumberPart) {
    var overflowDigitsCount = nationalSignificantNumberPart.length - getMaxNumberLength(country, metadata);

    if (overflowDigitsCount > 0) {
      return number.slice(0, number.length - overflowDigitsCount);
    }
  }

  return number;
}

function getMaxNumberLength(country, metadata) {
  // Get "possible lengths" for a phone number of the country.
  metadata = new Metadata(metadata);
  metadata.country(country); // Return the last "possible length".

  return metadata.numberingPlan.possibleLengths()[metadata.numberingPlan.possibleLengths().length - 1];
} // If the phone number being input is an international one
// then tries to derive the country from the phone number.
// (regardless of whether there's any country currently selected)

/**
 * @param {string} partialE164Number - A possibly incomplete E.164 phone number.
 * @param {string?} country - Currently selected country.
 * @param {string[]?} countries - A list of available countries. If not passed then "all countries" are assumed.
 * @param {boolean} includeInternationalOption - Whether "International" country option is available.
 * @param  {object} metadata - `libphonenumber-js` metadata.
 * @return {string?}
 */


export function getCountryForPartialE164Number(partialE164Number, _ref4) {
  var country = _ref4.country,
      countries = _ref4.countries,
      required = _ref4.required,
      metadata = _ref4.metadata;

  if (partialE164Number === '+') {
    // Don't change the currently selected country yet.
    return country;
  }

  var derived_country = getCountryFromPossiblyIncompleteInternationalPhoneNumber(partialE164Number, metadata); // If a phone number is being input in international form
  // and the country can already be derived from it,
  // then select that country.

  if (derived_country && (!countries || countries.indexOf(derived_country) >= 0)) {
    return derived_country;
  } // If "International" country option has not been disabled
  // and the international phone number entered doesn't correspond
  // to the currently selected country then reset the currently selected country.
  else if (country && !required && !couldNumberBelongToCountry(partialE164Number, country, metadata)) {
      return undefined;
    } // Don't change the currently selected country.


  return country;
}
/**
 * Parses `<input/>` value. Derives `country` from `input`. Derives an E.164 `value`.
 * @param  {string?} phoneDigits — Parsed `<input/>` value. Examples: `""`, `"+"`, `"+123"`, `"123"`.
 * @param  {string?} prevPhoneDigits — Previous parsed `<input/>` value. Examples: `""`, `"+"`, `"+123"`, `"123"`.
 * @param  {string?} country - Currently selected country.
 * @param  {string[]?} countries - A list of available countries. If not passed then "all countries" are assumed.
 * @param  {boolean} includeInternationalOption - Whether "International" country option is available.
 * @param  {boolean} international - Set to `true` to force international phone number format (leading `+`). Set to `false` to force "national" phone number format. Is `undefined` by default.
 * @param  {boolean} limitMaxLength — Whether to enable limiting phone number max length.
 * @param  {object} metadata - `libphonenumber-js` metadata.
 * @return {object} An object of shape `{ input, country, value }`.
 */

export function onPhoneDigitsChange(phoneDigits, _ref5) {
  var prevPhoneDigits = _ref5.prevPhoneDigits,
      country = _ref5.country,
      defaultCountry = _ref5.defaultCountry,
      countries = _ref5.countries,
      international = _ref5.international,
      limitMaxLength = _ref5.limitMaxLength,
      countryCallingCodeEditable = _ref5.countryCallingCodeEditable,
      metadata = _ref5.metadata;

  if (international && countryCallingCodeEditable === false) {
    var prefix = getInternationalPhoneNumberPrefix(country, metadata); // The `<input/>` value must start with the country calling code.

    if (phoneDigits.indexOf(prefix) !== 0) {
      return {
        phoneDigits: prefix,
        value: undefined,
        country: country
      };
    }
  } // If `international` property is `false`, then it means
  // "enforce national-only format during input",
  // so, if that's the case, then remove all `+` characters,
  // but only if some country is currently selected.
  // (not if "International" country is selected).


  if (international === false && country && phoneDigits && phoneDigits[0] === '+') {
    phoneDigits = convertInternationalPhoneDigitsToNational(phoneDigits, country, metadata);
  } // Trim the input to not exceed the maximum possible number length.


  if (phoneDigits && country && limitMaxLength) {
    phoneDigits = trimNumber(phoneDigits, country, metadata);
  } // If this `onChange()` event was triggered
  // as a result of selecting "International" country,
  // then force-prepend a `+` sign if the phone number
  // `<input/>` value isn't in international format.
  // Also, force-prepend a `+` sign if international
  // phone number input format is set.


  if (phoneDigits && phoneDigits[0] !== '+' && (!country || international)) {
    phoneDigits = '+' + phoneDigits;
  } // If the previously entered phone number
  // has been entered in international format
  // and the user decides to erase it,
  // then also reset the `country`
  // because it was most likely automatically selected
  // while the user was typing in the phone number
  // in international format.
  // This fixes the issue when a user is presented
  // with a phone number input with no country selected
  // and then types in their local phone number
  // then discovers that the input's messed up
  // (a `+` has been prepended at the start of their input
  //  and a random country has been selected),
  // decides to undo it all by erasing everything
  // and then types in their local phone number again
  // resulting in a seemingly correct phone number
  // but in reality that phone number has incorrect country.
  // https://github.com/catamphetamine/react-phone-number-input/issues/273


  if (!phoneDigits && prevPhoneDigits && prevPhoneDigits[0] === '+') {
    if (international) {
      country = undefined;
    } else {
      country = defaultCountry;
    }
  } // Also resets such "randomly" selected country
  // as soon as the user erases the number
  // digit-by-digit up to the leading `+` sign.


  if (phoneDigits === '+' && prevPhoneDigits && prevPhoneDigits[0] === '+' && prevPhoneDigits.length > '+'.length) {
    country = undefined;
  } // Generate the new `value` property.


  var value;

  if (phoneDigits) {
    if (phoneDigits[0] === '+') {
      if (phoneDigits === '+') {
        value = undefined;
      } else if (country && getInternationalPhoneNumberPrefix(country, metadata).indexOf(phoneDigits) === 0) {
        value = undefined;
      } else {
        value = phoneDigits;
      }
    } else {
      value = e164(phoneDigits, country, metadata);
    }
  } // Derive the country from the phone number.
  // (regardless of whether there's any country currently selected,
  //  because there could be several countries corresponding to one country calling code)


  if (value) {
    country = getCountryForPartialE164Number(value, {
      country: country,
      countries: countries,
      metadata: metadata
    }); // If `international` property is `false`, then it means
    // "enforce national-only format during input",
    // so, if that's the case, then remove all `+` characters,
    // but only if some country is currently selected.
    // (not if "International" country is selected).

    if (international === false && country && phoneDigits && phoneDigits[0] === '+') {
      phoneDigits = convertInternationalPhoneDigitsToNational(phoneDigits, country, metadata); // Re-calculate `value` because `phoneDigits` has changed.

      value = e164(phoneDigits, country, metadata);
    }
  }

  return {
    phoneDigits: phoneDigits,
    country: country,
    value: value
  };
}

function convertInternationalPhoneDigitsToNational(input, country, metadata) {
  // Handle the case when a user might have pasted
  // a phone number in international format.
  if (input.indexOf(getInternationalPhoneNumberPrefix(country, metadata)) === 0) {
    // Create "as you type" formatter.
    var formatter = new AsYouType(country, metadata); // Input partial national phone number.

    formatter.input(input); // Return the parsed partial national phone number.

    var phoneNumber = formatter.getNumber();

    if (phoneNumber) {
      // Transform the number to a national one,
      // and remove all non-digits.
      return phoneNumber.formatNational().replace(/\D/g, '');
    } else {
      return '';
    }
  } else {
    // Just remove the `+` sign.
    return input.replace(/\D/g, '');
  }
}
/**
 * Determines the country for a given (possibly incomplete) E.164 phone number.
 * @param  {string} number - A possibly incomplete E.164 phone number.
 * @param  {object} metadata - `libphonenumber-js` metadata.
 * @return {string?}
 */


export function getCountryFromPossiblyIncompleteInternationalPhoneNumber(number, metadata) {
  var formatter = new AsYouType(null, metadata);
  formatter.input(number); // // `001` is a special "non-geograpical entity" code
  // // in Google's `libphonenumber` library.
  // if (formatter.country === '001') {
  // 	return
  // }

  return formatter.country;
}
/**
 * Compares two strings.
 * A helper for `Array.sort()`.
 * @param {string} a — First string.
 * @param {string} b — Second string.
 * @param {(string[]|string)} [locales] — The `locales` argument of `String.localeCompare`.
 */

export function compareStrings(a, b, locales) {
  // Use `String.localeCompare` if it's available.
  // https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
  // Which means everyone except IE <= 10 and Safari <= 10.
  // `localeCompare()` is available in latest Node.js versions.

  /* istanbul ignore else */
  if (String.prototype.localeCompare) {
    return a.localeCompare(b, locales);
  }
  /* istanbul ignore next */


  return a < b ? -1 : a > b ? 1 : 0;
}
/**
 * Strips `+${countryCallingCode}` prefix from an E.164 phone number.
 * @param {string} number - (possibly incomplete) E.164 phone number.
 * @param {string?} country - A possible country for this phone number.
 * @param {object} metadata - `libphonenumber-js` metadata.
 * @return {string}
 */

export function stripCountryCallingCode(number, country, metadata) {
  // Just an optimization, so that it
  // doesn't have to iterate through all country calling codes.
  if (country) {
    var countryCallingCodePrefix = '+' + getCountryCallingCode(country, metadata); // If `country` fits the actual `number`.

    if (number.length < countryCallingCodePrefix.length) {
      if (countryCallingCodePrefix.indexOf(number) === 0) {
        return '';
      }
    } else {
      if (number.indexOf(countryCallingCodePrefix) === 0) {
        return number.slice(countryCallingCodePrefix.length);
      }
    }
  } // If `country` doesn't fit the actual `number`.
  // Try all available country calling codes.


  for (var _i = 0, _Object$keys = Object.keys(metadata.country_calling_codes); _i < _Object$keys.length; _i++) {
    var country_calling_code = _Object$keys[_i];

    if (number.indexOf(country_calling_code) === '+'.length) {
      return number.slice('+'.length + country_calling_code.length);
    }
  }

  return '';
}
/**
 * Parses a partially entered national phone number digits
 * (or a partially entered E.164 international phone number)
 * and returns the national significant number part.
 * National significant number returned doesn't come with a national prefix.
 * @param {string} number - National number digits. Or possibly incomplete E.164 phone number.
 * @param {string?} country
 * @param {object} metadata - `libphonenumber-js` metadata.
 * @return {string} [result]
 */

export function getNationalSignificantNumberDigits(number, country, metadata) {
  // Create "as you type" formatter.
  var formatter = new AsYouType(country, metadata); // Input partial national phone number.

  formatter.input(number); // Return the parsed partial national phone number.

  var phoneNumber = formatter.getNumber();
  return phoneNumber && phoneNumber.nationalNumber;
}
/**
 * Checks if a partially entered E.164 phone number could belong to a country.
 * @param  {string} number
 * @param  {string} country
 * @return {boolean}
 */

export function couldNumberBelongToCountry(number, country, metadata) {
  var intlPhoneNumberPrefix = getInternationalPhoneNumberPrefix(country, metadata);
  var i = 0;

  while (i < number.length && i < intlPhoneNumberPrefix.length) {
    if (number[i] !== intlPhoneNumberPrefix[i]) {
      return false;
    }

    i++;
  }

  return true;
}
/**
 * Gets initial "phone digits" (including `+`, if using international format).
 * @return {string} [phoneDigits] Returns `undefined` if there should be no initial "phone digits".
 */

export function getInitialPhoneDigits(_ref6) {
  var value = _ref6.value,
      phoneNumber = _ref6.phoneNumber,
      defaultCountry = _ref6.defaultCountry,
      international = _ref6.international,
      useNationalFormat = _ref6.useNationalFormat,
      metadata = _ref6.metadata;

  // If the `value` (E.164 phone number)
  // belongs to the currently selected country
  // and `useNationalFormat` is `true`
  // then convert `value` (E.164 phone number)
  // to a local phone number digits.
  // E.g. '+78005553535' -> '88005553535'.
  if ((international === false || useNationalFormat) && phoneNumber && phoneNumber.country) {
    return generateNationalNumberDigits(phoneNumber);
  } // If `international` property is `true`,
  // meaning "enforce international phone number format",
  // then always show country calling code in the input field.


  if (!value && international && defaultCountry) {
    return getInternationalPhoneNumberPrefix(defaultCountry, metadata);
  }

  return value;
}
//# sourceMappingURL=phoneInputHelpers.js.map