//! Conformance corpus for the tensor-file prior forms (`live-conformance/prior-forms/`,
//! extensions/prior-carriage.md §14).
//!
//!   wai_prior_forms_vectors gen    <corpus-dir>   write vectors.json and files/ from the native
//!                                                 artifacts other corpora commit, which must
//!                                                 match their pins
//!   wai_prior_forms_vectors verify <corpus-dir>   the natives match their pins, every file
//!                                                 regenerates byte for byte, no other file but
//!                                                 README.md is there, and every vector gets
//!                                                 the outcome it states
//!
//! The generator and this verifier are one implementation. The corpus's second
//! implementation is `live-conformance/prior_forms_verify.py`, written from the extension's
//! text and sharing no code with this binary.

use std::collections::BTreeMap;
use std::path::Path;
use wai::prior_wire::corpus;

fn walk(root: &Path, rel: &str, out: &mut BTreeMap<String, Vec<u8>>) -> Result<(), String> {
    let d = root.join(rel);
    let entries = std::fs::read_dir(&d).map_err(|e| format!("read {}: {e}", d.display()))?;
    for e in entries.flatten() {
        let name = e.file_name().to_string_lossy().into_owned();
        let key = if rel.is_empty() { name.clone() } else { format!("{rel}/{name}") };
        if e.path().is_dir() {
            walk(root, &key, out)?;
        } else if key != "README.md" && key != ".gitattributes" {
            out.insert(key, std::fs::read(e.path()).map_err(|err| format!("read {}: {err}", e.path().display()))?);
        }
    }
    Ok(())
}

fn natives(dir: &Path) -> BTreeMap<String, Vec<u8>> {
    corpus::INPUT_NATIVES.iter().filter_map(|(n, _, p, _)| std::fs::read(dir.join(p)).ok().map(|b| (n.to_string(), b))).collect()
}

fn die(e: impl std::fmt::Display) -> ! {
    eprintln!("{e}");
    std::process::exit(1)
}

fn main() {
    let a: Vec<String> = std::env::args().collect();
    match (a.get(1).map(String::as_str), a.get(2)) {
        (Some("gen"), Some(d)) => {
            let dir = Path::new(d);
            let nat = natives(dir);
            let bad = corpus::check_inputs(&nat);
            if !bad.is_empty() {
                die(format!("the natives {bad:?} are missing or do not match their pins in corpus::INPUT_NATIVES"));
            }
            for (name, body) in corpus::generate(&nat) {
                let p = dir.join(&name);
                if let Some(parent) = p.parent() {
                    std::fs::create_dir_all(parent).unwrap_or_else(|e| die(format!("mkdir {}: {e}", parent.display())));
                }
                std::fs::write(&p, body).unwrap_or_else(|e| die(format!("write {}: {e}", p.display())));
            }
            println!("wrote the prior-forms corpus to {d}");
        }
        (Some("verify"), Some(d)) => {
            let dir = Path::new(d);
            let mut files = BTreeMap::new();
            walk(dir, "", &mut files).unwrap_or_else(|e| die(e));
            let bad = corpus::verify(&files, &natives(dir));
            if !bad.is_empty() {
                for b in &bad {
                    eprintln!("FAIL {b}");
                }
                std::process::exit(1);
            }
            println!("prior-forms corpus: {} files regenerate byte for byte and every vector reaches its outcome", files.len());
        }
        _ => die("usage: wai_prior_forms_vectors gen|verify <corpus-dir>"),
    }
}
