use std::{
    collections::VecDeque,
    path::{Path, PathBuf},
};

use blake3::{Hash, Hasher};

pub fn hash_file(path: impl AsRef<Path>) -> std::io::Result<Hash> {
    let mut hasher = Hasher::new();
    hasher.update_mmap(path)?;
    Ok(hasher.finalize())
}

pub fn recurse_list_from_root<P: AsRef<Path>>(
    root: P,
    yield_dirs: bool,
) -> impl Iterator<Item = PathBuf> {
    let mut stack = VecDeque::new();
    stack.push_back(root.as_ref().to_owned());

    std::iter::from_fn(move || {
        while let Some(path) = stack.pop_back() {
            if path.is_dir() {
                if let Ok(entries) = path.read_dir() {
                    let filtered = entries.filter_map(Result::ok).map(|a| a.path());

                    stack.extend(filtered);
                }

                if yield_dirs {
                    return Some(path);
                }
            } else if path.is_file() {
                return Some(path);
            }
        }

        None
    })
}

pub fn recurse_list_dirs<P: AsRef<Path>>(root: P) -> impl Iterator<Item = PathBuf> {
    let mut stack = VecDeque::new();
    stack.push_back(root.as_ref().to_owned());

    std::iter::from_fn(move || {
        while let Some(path) = stack.pop_back() {
            if path.is_dir() {
                if let Ok(entries) = path.read_dir() {
                    let filtered = entries.filter_map(Result::ok).map(|a| a.path());
                    stack.extend(filtered);
                }

                return Some(path);
            }
        }

        None
    })
}
