use rand::{prelude::SliceRandom, rngs::OsRng, Rng};

const LETTERS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
const CAPITAL_LETTERS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const NUMBERS: &[u8] = b"0123456789";
const SYMBOLS: &[u8] = br#" !#$%"&'()*+,-./:;<=>?@[\]^_`{|}~"#;

pub struct PasswordGenerator {
    min_length: u16,
    max_length: u16,
    letters: bool,
    capital_letters: bool,
    numbers: bool,
    symbols: bool,
}

impl PasswordGenerator {
    pub fn new_exact_length(
        length: u16,
        letters: bool,
        capital_letters: bool,
        numbers: bool,
        symbols: bool,
    ) -> Self {
        Self::new(length, length, letters, capital_letters, numbers, symbols)
    }

    pub fn new(
        min_length: u16,
        max_length: u16,
        letters: bool,
        capital_letters: bool,
        numbers: bool,
        symbols: bool,
    ) -> Self {
        Self {
            min_length,
            max_length,
            letters,
            capital_letters,
            numbers,
            symbols,
        }
    }

    pub fn generate(&self) -> String {
        let length = OsRng.gen_range(self.min_length..=self.max_length) as usize;

        let set = [
            self.letters.then(|| LETTERS.iter()),
            self.capital_letters.then(|| CAPITAL_LETTERS.iter()),
            self.numbers.then(|| NUMBERS.iter()),
            self.symbols.then(|| SYMBOLS.iter()),
        ]
        .into_iter()
        .flatten()
        .flatten()
        .collect::<Vec<_>>();

        let mut bytes = std::iter::repeat_with(|| **set.choose(&mut OsRng).unwrap())
            .take(length)
            .collect::<Vec<_>>();
        bytes.shuffle(&mut OsRng);

        String::from_utf8(bytes).expect("password bytes to utf8 string.")
    }

    pub fn generate_n(&self, n: usize) -> Vec<String> {
        std::iter::repeat_with(|| self.generate()).take(n).collect()
    }
}
