//! File-based tests for the `PlantUML` formatter (`Network::to_plantuml`, `README_INTEGRATION.md`
//! Layer 4c).
//!
//! Each test is a pair of files in `tests/format/plantuml/`: `<name>.nxs` (the program) and
//! `<name>.puml` (the expected `PlantUML` text, byte-for-byte). Output is deterministic given
//! `NodeId`s are assigned by a monotonic counter in declaration order (see `README_INTEGRATION.md`
//! R1), so no normalization is needed before comparing.
//!
//! Add a test by dropping a `<name>.nxs` / `<name>.puml` pair into `tests/format/plantuml/`; no Rust
//! changes needed. Regenerate an expected file after an intentional formatter change with:
//! `cargo run --bin nexus -- -f nexus-lang/tests/format/plantuml/<name>.nxs --output plantuml`.

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

const TEST_DIR: &str = "tests/format/plantuml";

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

fn collect_tests() -> Vec<Trial> {
    let mut paths = index_nxs_files(Path::new(TEST_DIR));
    paths.sort();

    paths
        .into_iter()
        .map(|nxs| {
            let stem = nxs.file_stem().unwrap().to_string_lossy().into_owned();
            let expected = nxs.with_extension("puml");
            Trial::test(format!("plantuml/{stem}"), move || {
                run_case(&nxs, &expected)
            })
        })
        .collect()
}

/// `.nxs` files in `dir`, excluding `_lib` companion files (reachable via `use`, not tests).
fn index_nxs_files(dir: &Path) -> Vec<PathBuf> {
    fs::read_dir(dir)
        .unwrap_or_else(|_| panic!("Failed to read directory {}", dir.display()))
        .filter_map(Result::ok)
        .map(|e| e.path())
        .filter(|p| {
            p.extension().is_some_and(|e| e == "nxs")
                && !p
                    .file_stem()
                    .and_then(|s| s.to_str())
                    .is_some_and(|s| s.contains("_lib"))
        })
        .collect()
}

fn run_case(nxs: &Path, expected_path: &Path) -> Result<(), Failed> {
    let (network, _warnings) = Nexus::run_file(nxs).map_err(|e| format!("{e:?}"))?;
    let actual = network.to_plantuml();

    let expected = fs::read_to_string(expected_path).map_err(|_| {
        Failed::from(format!(
            "missing expected-output file: {}",
            expected_path.display()
        ))
    })?;

    pretty_assertions::assert_eq!(expected, actual);
    Ok(())
}
