package com.airlock.iam.security.util.crypto.random;

import com.airlock.iam.security.util.SeededSecureRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.random.RandomGenerator;
import java.util.stream.IntStream;

public final class AlphanumericRandomStringGenerator {
   private static final String BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
   private static final char[] BASE58_ALPHABET_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
   public static final String FULL_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
   private static final char[] FULL_ALPHABET_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

   public static String generateAlphanumericRandomString(int length) {
      return generateRandomString(length, FULL_ALPHABET_CHARS, SeededSecureRandom.getInstance());
   }

   public static String generateAlphanumericRandomString(int length, RandomGenerator random) {
      return generateRandomString(length, FULL_ALPHABET_CHARS, random);
   }

   public static String generateHumanSafeAlphanumericRandomString(int length) {
      return generateRandomString(length, BASE58_ALPHABET_CHARS, SeededSecureRandom.getInstance());
   }

   private static String generateRandomString(int length, char[] alphabet, RandomGenerator random) {
      IntStream ints = random.ints(length, 0, alphabet.length);
      char[] chars = new char[length];
      AtomicInteger i = new AtomicInteger();
      ints.forEach(index -> chars[i.getAndIncrement()] = alphabet[index]);
      return new String(chars);
   }

   public static boolean isValidAlphanumericRandomString(String randomString) {
      return randomString.codePoints()
         .allMatch(codePoint -> "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".contains(Character.toString(codePoint)));
   }

   private AlphanumericRandomStringGenerator() {
      throw new AssertionError();
   }
}
