use std::path::PathBuf;

use anyhow::Context;
use crossbeam::channel::Sender;
use ignore::{Error, WalkBuilder, WalkState};

use crate::candidate_selection::messages::FileWalkerMessage;
use crate::candidate_selection::messages::FileWalkerMessage::{FileFound, WalkCompleted};

pub fn walk_paths(
    path_vec: &[PathBuf],
    file_event_sender: Sender<FileWalkerMessage>,
) -> anyhow::Result<()> {
    let mut path_iter = path_vec.iter();

    let first_path_buf = path_iter
        .next()
        .expect("Error: Path vector can not be empty");

    let mut walker = WalkBuilder::new(first_path_buf.as_path());

    for path_entry in path_iter {
        walker.add(path_entry.as_path());
    }

    let parallel_walker = walker.threads(2).build_parallel();

    parallel_walker.run(|| {
        let file_event_sender = file_event_sender.clone();

        Box::new(move |entry| match entry {
            Ok(entry) => {
                if let Some(file_type) = entry.file_type() {
                    if file_type.is_symlink() {
                        return WalkState::Skip;
                    }

                    if file_type.is_dir() {
                        return WalkState::Continue;
                    }

                    return match file_event_sender.send(FileFound(entry.into_path())) {
                        Ok(_) => WalkState::Continue,
                        _ => WalkState::Quit,
                    };
                }

                WalkState::Continue
            }

            Err(e) => {
                if let Error::WithPath { path, .. } = e {
                    eprintln!(
                        "Unable to read {}",
                        path.to_str().unwrap_or("<unknown path>")
                    )
                }

                WalkState::Continue
            }
        })
    });

    info!("Sending WalkCompleted");
    file_event_sender
        .send(WalkCompleted)
        .context("File events closed")?;

    info!("Path walking finished");

    Ok(())
}
