use libtest_mimic::{Arguments, Failed, Trial};
use nexus_lang::{Scanner, Tokens};
use std::{
    fs,
    path::{Path, PathBuf},
};

fn main() {
    let args = Arguments::from_args();
    libtest_mimic::run(&args, collect_tests()).exit();
}

fn collect_tests() -> Vec<Trial> {
    let mut tests = Vec::new();
    add_trials(&mut tests, "tests/scanner/pass", true);
    add_trials(&mut tests, "tests/scanner/fail", false);
    tests
}

fn add_trials(tests: &mut Vec<Trial>, dir: &str, should_pass: bool) {
    let mut paths = index_nxs_files(Path::new(dir));
    paths.sort();

    for path in paths {
        let stem = path.file_stem().unwrap().to_string_lossy().into_owned();
        let label = if should_pass { "pass" } else { "fail" };
        let name = format!("scanner/{label}/{stem}");

        tests.push(Trial::test(name, move || {
            let result = run_program(&path);
            if should_pass {
                result.map(|_| ()).map_err(Failed::from)
            } else {
                result
                    .err()
                    .map(|_| ())
                    .ok_or_else(|| Failed::from("expected failure but program succeeded"))
            }
        }));
    }
}

fn index_nxs_files(dir: &Path) -> Vec<PathBuf> {
    fs::read_dir(dir)
        .unwrap_or_else(|_| panic!("Failed to read directory {dir:?}"))
        .filter_map(Result::ok)
        .map(|e| e.path())
        .filter(|p| p.extension().is_some_and(|e| e == "nxs"))
        .collect()
}

fn run_program(program_path: &Path) -> Result<Tokens, String> {
    let source =
        fs::read_to_string(program_path).map_err(|e| format!("Failed to read file: {e}"))?;
    let scanner = Scanner::new((&source).into());
    scanner.scan().map_err(|e| format!("{e:?}"))
}
