use std::{
    fs,
    path::Path,
    process::{Command, Output},
};

#[test]
fn array_enum_fixture_emits_enum_array_llvm_shapes() {
    let output = run_glagol(["../examples/array-enum.slo"]);
    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);

    assert!(
        output.status.success(),
        "compiler rejected array-enum fixture\nstdout:\n{}\nstderr:\n{}",
        stdout,
        stderr
    );
    assert!(
        stdout.contains("define [3 x i32] @make_palette()")
            && stdout.contains("define i32 @at([3 x i32] %colors, i32 %i)")
            && stdout.contains("alloca [3 x i32]")
            && stdout.contains("call void @__glagol_array_bounds_trap()"),
        "LLVM output did not contain expected enum-array shapes\nstdout:\n{}",
        stdout
    );
    assert!(stderr.is_empty(), "compiler wrote stderr:\n{}", stderr);
}

#[test]
fn array_enum_fixture_runs_tests_and_formats_stably() {
    let run = run_glagol(["--run-tests", "../examples/array-enum.slo"]);
    assert_success_stdout(
        run,
        concat!(
            "test \"enum array immediate index\" ... ok\n",
            "test \"enum array local index\" ... ok\n",
            "test \"enum array param return dynamic index\" ... ok\n",
            "3 test(s) passed\n",
        ),
        "array-enum test runner output",
    );

    let expected = fs::read_to_string("../tests/array-enum.slo").expect("read fixture");
    let format = run_glagol(["--format", "../tests/array-enum.slo"]);
    assert_success_stdout(format, &expected, "array-enum formatter output");
}

#[test]
fn array_enum_fixture_lowering_snapshots_are_stable() {
    let surface = run_glagol(["--inspect-lowering=surface", "../tests/array-enum.slo"]);
    assert_success_stdout(
        surface,
        &fs::read_to_string("../tests/array-enum.surface.lower").expect("read surface snapshot"),
        "array-enum surface lowering output",
    );

    let checked = run_glagol(["--inspect-lowering=checked", "../tests/array-enum.slo"]);
    assert_success_stdout(
        checked,
        &fs::read_to_string("../tests/array-enum.checked.lower").expect("read checked snapshot"),
        "array-enum checked lowering output",
    );
}

fn run_glagol<const N: usize>(args: [&str; N]) -> Output {
    Command::new(env!("CARGO_BIN_EXE_glagol"))
        .args(args)
        .current_dir(Path::new(env!("CARGO_MANIFEST_DIR")))
        .output()
        .expect("run glagol")
}

fn assert_success_stdout(output: Output, expected: &str, context: &str) {
    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(
        output.status.success(),
        "{} failed\nstdout:\n{}\nstderr:\n{}",
        context,
        stdout,
        stderr
    );
    assert_eq!(stdout, expected, "{} drifted", context);
    assert!(stderr.is_empty(), "{} wrote stderr:\n{}", context, stderr);
}
