//! The corpus of extensions/channel-lineup.md §11 (`live-conformance/channels/`).
//!
//!   wai_channel_lineup_vectors gen <dir>      write every file from the fixed seeds
//!   wai_channel_lineup_vectors verify <dir>   require byte-identical regeneration, no other
//!                                             file but the README, and every vector's outcome
//!
//! Exit status 0 when it holds, 1 with one line a finding otherwise, 2 on a usage error.

use std::collections::BTreeMap;
use std::path::Path;
use std::process::ExitCode;

use wai::channel_lineup::corpus;

fn main() -> ExitCode {
    let args: Vec<String> = std::env::args().skip(1).collect();
    let (Some(mode), Some(dir)) = (args.first(), args.get(1)) else {
        eprintln!("usage: wai_channel_lineup_vectors gen|verify <dir>");
        return ExitCode::from(2);
    };
    let dir = Path::new(dir);
    let generated = corpus::generate();
    match mode.as_str() {
        "gen" => {
            if let Err(e) = std::fs::create_dir_all(dir) {
                eprintln!("{}: {e}", dir.display());
                return ExitCode::from(1);
            }
            for (name, body) in &generated {
                if let Err(e) = std::fs::write(dir.join(name), body) {
                    eprintln!("{name}: {e}");
                    return ExitCode::from(1);
                }
            }
            println!("wrote {} files to {}", generated.len(), dir.display());
            ExitCode::SUCCESS
        }
        "verify" => {
            let mut committed = BTreeMap::new();
            let entries = match std::fs::read_dir(dir) {
                Ok(e) => e,
                Err(e) => {
                    eprintln!("{}: {e}", dir.display());
                    return ExitCode::from(1);
                }
            };
            for e in entries.flatten() {
                let name = e.file_name().to_string_lossy().into_owned();
                if name == "README.md" {
                    continue;
                }
                match std::fs::read_to_string(e.path()) {
                    Ok(s) => {
                        committed.insert(name, s);
                    }
                    Err(err) => {
                        eprintln!("{name}: {err}");
                        return ExitCode::from(1);
                    }
                }
            }
            let mut fails: Vec<String> = Vec::new();
            for (name, body) in &generated {
                match committed.get(name) {
                    None => fails.push(format!("{name}: missing")),
                    Some(c) if c != body => fails.push(format!("{name}: does not regenerate byte for byte")),
                    Some(_) => {}
                }
            }
            for name in committed.keys().filter(|n| !generated.iter().any(|(g, _)| g == *n)) {
                fails.push(format!("{name}: not a file of the corpus"));
            }
            fails.extend(corpus::verify(&committed));
            for f in &fails {
                println!("{f}");
            }
            if fails.is_empty() {
                println!("channel lineup corpus: {} files regenerate and every vector holds", generated.len());
                ExitCode::SUCCESS
            } else {
                ExitCode::from(1)
            }
        }
        _ => {
            eprintln!("usage: wai_channel_lineup_vectors gen|verify <dir>");
            ExitCode::from(2)
        }
    }
}
