class CleanPasswords {
  constructor({
    substr,
    limit,
    trieRoot
  }) {
    this.buffer = [];
    this.finalPasswords = [];
    this.substr = substr;
    this.limit = limit;
    this.trieRoot = trieRoot;
  }
  getAllPossibleSubsAtIndex(index) {
    const nodes = [];
    let cur = this.trieRoot;
    for (let i = index; i < this.substr.length; i += 1) {
      const character = this.substr.charAt(i);
      cur = cur.getChild(character);
      if (!cur) {
        break;
      }
      nodes.push(cur);
    }
    return nodes;
  }
  // eslint-disable-next-line complexity,max-statements
  helper({
    onlyFullSub,
    isFullSub,
    index,
    subIndex,
    changes,
    lastSubLetter,
    consecutiveSubCount
  }) {
    if (this.finalPasswords.length >= this.limit) {
      return;
    }
    if (index === this.substr.length) {
      if (onlyFullSub === isFullSub) {
        this.finalPasswords.push({
          password: this.buffer.join(''),
          changes,
          isFullSubstitution: onlyFullSub
        });
      }
      return;
    }
    // first, exhaust all possible substitutions at this index
    const nodes = [...this.getAllPossibleSubsAtIndex(index)];
    let hasSubs = false;
    // iterate backward to get wider substitutions first
    for (let i = index + nodes.length - 1; i >= index; i -= 1) {
      const cur = nodes[i - index];
      const sub = cur.parents.join('');
      if (cur.isTerminal()) {
        // Skip if this would be a 4th or more consecutive substitution of the same letter
        // this should work in all language as there shouldn't be the same letter more than four times in a row
        // So we can ignore the rest to save calculation time
        if (lastSubLetter === sub && consecutiveSubCount >= 3) {
          continue;
        }
        hasSubs = true;
        const letters = cur.subs;
        for (const letter of letters) {
          this.buffer.push(letter);
          const newSubs = changes.concat({
            i: subIndex,
            letter,
            substitution: sub
          });
          // recursively build the rest of the string
          this.helper({
            onlyFullSub,
            isFullSub,
            index: index + sub.length,
            subIndex: subIndex + letter.length,
            changes: newSubs,
            lastSubLetter: sub,
            consecutiveSubCount: lastSubLetter === sub ? consecutiveSubCount + 1 : 1
          });
          // backtrack by ignoring the added postfix
          this.buffer.pop();
          if (this.finalPasswords.length >= this.limit) {
            return;
          }
        }
      }
    }
    // next, generate all combos without doing a substitution at this index
    // if a partial substitution is requested or there are no substitutions at this index
    if (!onlyFullSub || !hasSubs) {
      const firstChar = this.substr.charAt(index);
      this.buffer.push(firstChar);
      this.helper({
        onlyFullSub,
        isFullSub: isFullSub && !hasSubs,
        index: index + 1,
        subIndex: subIndex + 1,
        changes,
        lastSubLetter,
        consecutiveSubCount
      });
      this.buffer.pop();
    }
  }
  getAll() {
    // only full substitution
    this.helper({
      onlyFullSub: true,
      isFullSub: true,
      index: 0,
      subIndex: 0,
      changes: [],
      lastSubLetter: undefined,
      consecutiveSubCount: 0
    });
    // only partial substitution
    this.helper({
      onlyFullSub: false,
      isFullSub: true,
      index: 0,
      subIndex: 0,
      changes: [],
      lastSubLetter: undefined,
      consecutiveSubCount: 0
    });
    return this.finalPasswords;
  }
}
const getCleanPasswords = (password, limit, trieRoot) => {
  const helper = new CleanPasswords({
    substr: password,
    limit,
    trieRoot
  });
  return helper.getAll();
};

export { getCleanPasswords as default };
//# sourceMappingURL=getCleanPasswords.mjs.map
