use std::{
    ffi::OsStr,
    path::{Path, PathBuf},
    process::{Command, Output},
};

#[test]
fn enum_payload_i32_alpha_fixture_emits_aggregate_enums_and_tests_pass() {
    let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples/enum-payload-i32.slo");
    let compile = run_glagol([fixture.as_os_str()]);
    let stdout = String::from_utf8_lossy(&compile.stdout);
    let stderr = String::from_utf8_lossy(&compile.stderr);

    assert!(
        compile.status.success(),
        "compiler rejected enum payload fixture\nstdout:\n{}\nstderr:\n{}",
        stdout,
        stderr
    );
    assert!(
        stdout.contains("define { i32, i32 } @value(i32 %payload)")
            && stdout.contains("switch i32")
            && stdout.contains("extractvalue { i32, i32 }")
            && stdout.contains("and i1"),
        "LLVM output did not contain expected enum payload aggregate shape\nstdout:\n{}",
        stdout
    );

    let run = run_glagol([OsStr::new("--run-tests"), fixture.as_os_str()]);
    assert_success_stdout(
        run,
        concat!(
            "test \"enum payload constructor equality\" ... ok\n",
            "test \"enum payload equality compares payload\" ... ok\n",
            "test \"enum payloadless equality\" ... ok\n",
            "test \"enum payload local return call flow\" ... ok\n",
            "test \"enum payload parameter equality\" ... ok\n",
            "test \"enum payload match missing\" ... ok\n",
            "test \"enum payload match value\" ... ok\n",
            "test \"enum payload match offset\" ... ok\n",
            "8 test(s) passed\n",
        ),
    );
}

#[test]
fn enum_payload_i32_alpha_formatter_and_lowering_are_visible() {
    let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/enum-payload-i32.slo");

    let formatted = run_glagol([OsStr::new("--format"), fixture.as_os_str()]);
    assert_success_stdout(
        formatted,
        &std::fs::read_to_string(&fixture).expect("read enum payload fixture"),
    );

    let surface = run_glagol([
        OsStr::new("--inspect-lowering=surface"),
        fixture.as_os_str(),
    ]);
    assert_success_stdout(
        surface,
        &std::fs::read_to_string(
            Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/enum-payload-i32.surface.lower"),
        )
        .expect("read surface snapshot"),
    );

    let checked = run_glagol([
        OsStr::new("--inspect-lowering=checked"),
        fixture.as_os_str(),
    ]);
    assert_success_stdout(
        checked,
        &std::fs::read_to_string(
            Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/enum-payload-i32.checked.lower"),
        )
        .expect("read checked snapshot"),
    );
}

fn run_glagol<I, S>(args: I) -> Output
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    Command::new(glagol_bin())
        .args(args)
        .output()
        .expect("run glagol")
}

fn glagol_bin() -> PathBuf {
    PathBuf::from(env!("CARGO_BIN_EXE_glagol"))
}

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