use std::sync::OnceLock;

pub(super) const WORDS: &str = include_str!("../data/words.txt");
pub(super) const NAMES: &str = include_str!("../data/names.txt");

pub(super) fn combined(min_len: usize, max_len: usize) -> impl Iterator<Item = &'static str> {
    iter_range(combined_entries(), min_len, max_len)
}

pub(super) fn only_words(min_len: usize, max_len: usize) -> impl Iterator<Item = &'static str> {
    iter_range(word_entries(), min_len, max_len)
}

pub(super) fn only_names(min_len: usize, max_len: usize) -> impl Iterator<Item = &'static str> {
    iter_range(name_entries(), min_len, max_len)
}

fn combined_entries() -> &'static [&'static str] {
    static CACHE: OnceLock<Vec<&'static str>> = OnceLock::new();
    CACHE.get_or_init(|| load_entries(WORDS.lines().chain(NAMES.lines()))).as_slice()
}

fn word_entries() -> &'static [&'static str] {
    static CACHE: OnceLock<Vec<&'static str>> = OnceLock::new();
    CACHE.get_or_init(|| load_entries(WORDS.lines())).as_slice()
}

fn name_entries() -> &'static [&'static str] {
    static CACHE: OnceLock<Vec<&'static str>> = OnceLock::new();
    CACHE.get_or_init(|| load_entries(NAMES.lines())).as_slice()
}

fn load_entries<I>(iter: I) -> Vec<&'static str>
where
    I: Iterator<Item = &'static str>,
{
    let mut entries: Vec<&'static str> =
        iter.map(str::trim).filter(|word| !word.is_empty()).collect();

    entries.sort_unstable_by(|a, b| a.len().cmp(&b.len()).then(a.cmp(b)));
    entries.dedup();
    entries
}

fn iter_range(
    entries: &'static [&'static str],
    min_len: usize,
    max_len: usize,
) -> impl Iterator<Item = &'static str> {
    if min_len > max_len {
        return [].iter().copied();
    }

    let start = entries.partition_point(|word| word.len() < min_len);
    let end = entries.partition_point(|word| word.len() <= max_len);

    entries[start..end].iter().copied()
}
