import { DIGIT_PLACEHOLDER, DUMMY_DIGIT, FIRST_GROUP_ONLY_PREFIX_PATTERN, LONGEST_NATIONAL_NUMBER_LENGTH, MIN_LEADING_DIGITS_LENGTH, } from './types';
// ---------------------------------------------------------------------------
// Format-rule inspection
// ---------------------------------------------------------------------------
/**
 * Check whether a format's replacement template only uses captured groups
 * (doesn't insert extra digits). This mirrors NON_ALTERING_FORMAT_REG_EXP
 * in the original library.
 *
 * @param {string} replacement - the replacement string to test
 * @returns {boolean} true if the format is non-altering
 */
export function isNonAlteringFormat(replacement) {
  return /^[\s\-().]*\$1[\s\-().]*(\$\d[\s\-().]*)*$/.test(replacement);
}
/**
 * Returns true if the format actually uses a national prefix when formatting.
 * A format "uses" a national prefix if it has a nationalPrefixFormattingRule
 * that is NOT just ($1) or $1.
 *
 * @param {FormatRule} formatRule - the format rule to inspect
 * @returns {boolean} true if the format uses a national prefix
 */
function formatUsesNationalPrefix(formatRule) {
  const rule = formatRule[3];
  if (!rule)
    return false;
  if (FIRST_GROUP_ONLY_PREFIX_PATTERN.test(rule))
    return false;
  return true;
}
/**
 * Returns true if the national prefix is optional when formatting.
 * In the metadata, format[4] being truthy indicates optional.
 *
 * @param {FormatRule} formatRule - the format rule to inspect
 * @returns {boolean} true if the national prefix is optional
 */
function formatNationalPrefixIsOptional(formatRule) {
  return !!formatRule[4];
}
/**
 * Returns true if the national prefix is mandatory for this format.
 * Mandatory = uses national prefix AND it's not optional.
 *
 * @param {FormatRule} formatRule - the format rule to inspect
 * @returns {boolean} true if the national prefix is mandatory
 */
function formatNationalPrefixIsMandatory(formatRule) {
  return formatUsesNationalPrefix(formatRule) && !formatNationalPrefixIsOptional(formatRule);
}
/**
 * Check if a format is suitable given the current formatting context.
 * Mirrors formatSuits() from the original library's AsYouTypeFormatter.
 *
 * @param {FormatRule} formatRule - the format rule to check
 * @param {boolean} international - whether the number is international
 * @param {boolean} nationalPrefix - whether a national prefix is present
 * @returns {boolean} true if the format is suitable
 */
export function formatSuits(formatRule, international, nationalPrefix) {
  if (nationalPrefix &&
    !formatUsesNationalPrefix(formatRule) &&
    !formatNationalPrefixIsOptional(formatRule)) {
    return false;
  }
  if (!international && !nationalPrefix && formatNationalPrefixIsMandatory(formatRule)) {
    return false;
  }
  return true;
}
// ---------------------------------------------------------------------------
// Leading-digits matching
// ---------------------------------------------------------------------------
/**
 * Check whether a format's leading digits match the given national number.
 *
 * @param {FormatRule} formatRule - the format rule to check
 * @param {string} nationalNumber - the national number digits
 * @param {number} leadingDigitsPatternIndex - index into the leading digits patterns
 * @returns {boolean} true if the leading digits match
 */
export function formatMatchesLeadingDigits(formatRule, nationalNumber, leadingDigitsPatternIndex) {
  const leadingDigitsPatterns = formatRule[2];
  if (!leadingDigitsPatterns || leadingDigitsPatterns.length === 0) {
    return true;
  }
  // Don't narrow down formats until we have enough leading digits.
  // This matches the original libphonenumber-js behavior where formats are
  // kept as candidates until MIN_LEADING_DIGITS_LENGTH digits are available.
  if (nationalNumber.length < MIN_LEADING_DIGITS_LENGTH) {
    return true;
  }
  const index = Math.min(leadingDigitsPatternIndex, leadingDigitsPatterns.length - 1);
  const pattern = leadingDigitsPatterns[index];
  return new RegExp('^(' + pattern + ')').test(nationalNumber);
}
// ---------------------------------------------------------------------------
// Template building and population
// ---------------------------------------------------------------------------
/**
 * Build a formatting template for a given format rule and national number.
 *
 * This replicates the template-generation logic from the original library:
 * 1. Generate a dummy number of 9s matching the format pattern.
 * 2. Format it with the replacement template.
 * 3. Replace each 9 with the DIGIT_PLACEHOLDER.
 *
 * @param {FormatRule} formatRule - the format rule to build a template from
 * @param {number} nationalSignificantNumberLength - length of the national significant number
 * @returns {string | undefined} the built template, or undefined if no match
 */
export function buildTemplate(formatRule, nationalSignificantNumberLength) {
  const [patternSrc, replacement] = formatRule;
  let pattern = patternSrc;
  // Simplify character classes and standalone digits to \d
  pattern = pattern.replace(/\[([^\]]*)\]/g, '\\d');
  pattern = pattern.replace(/\d(?=[^,}][^,}])/g, '\\d');
  const longestDummy = DUMMY_DIGIT.repeat(LONGEST_NATIONAL_NUMBER_LENGTH);
  const match = longestDummy.match(new RegExp(pattern));
  if (!match)
    return undefined;
  let digits = match[0];
  const strictPattern = new RegExp('^' + pattern + '$');
  const nationalDummy = DUMMY_DIGIT.repeat(nationalSignificantNumberLength);
  if (strictPattern.test(nationalDummy)) {
    digits = nationalDummy;
  }
  if (nationalSignificantNumberLength > digits.length) {
    return undefined;
  }
  const template = digits
    .replace(new RegExp(patternSrc), replacement)
    .replace(new RegExp(DUMMY_DIGIT, 'g'), DIGIT_PLACEHOLDER);
  return template;
}
/**
 * Cut a template at a given position, handling closing parentheses.
 * Matches the cut-and-strip logic from the original library.
 *
 * @param {string} str - the template string to cut
 * @param {number} cutBefore - the position to cut before
 * @returns {string} the cut and stripped template
 */
// eslint-disable-next-line spellcheck/spell-checker
export function cutAndStripNonPairedParens(str, cutBefore) {
  if (str[cutBefore] === ')') {
    cutBefore++;
  }
  // eslint-disable-next-line spellcheck/spell-checker
  return stripNonPairedParens(str.substring(0, cutBefore));
}
/**
 * Remove unpaired ( from a string.
 *
 * @param {string} str - the string to strip unpaired parentheses from
 * @returns {string} the string with unpaired open parentheses removed
 */
// eslint-disable-next-line spellcheck/spell-checker
function stripNonPairedParens(str) {
  const unmatchedOpenPositions = [];
  for (let i = 0; i < str.length; i++) {
    if (str[i] === '(') {
      unmatchedOpenPositions.push(i);
    }
    else if (str[i] === ')') {
      unmatchedOpenPositions.pop();
    }
  }
  let result = str;
  for (let i = unmatchedOpenPositions.length - 1; i >= 0; i--) {
    const position = unmatchedOpenPositions[i];
    result = result.substring(0, position) + result.substring(position + 1);
  }
  return result;
}
/**
 * Apply international separator style: replace parentheses and dashes with spaces.
 *
 * @param {string} template - the template string to transform
 * @returns {string} the template with international separator style applied
 */
// eslint-disable-next-line spellcheck/spell-checker
export function applyInternationalSeparatorStyle(template) {
  return template.replace(/[()]/g, '').replace(/[-]/g, ' ').replace(/ +/g, ' ').trim();
}
