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

#[test]
fn user_adts_alpha_fixture_emits_enum_discriminants_and_tests_pass() {
    let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples/enum-basic.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 fixture\nstdout:\n{}\nstderr:\n{}",
        stdout,
        stderr
    );
    assert!(
        stdout.contains("define i32 @signal_code(i32 %signal)")
            && stdout.contains("switch i32 %signal")
            && stdout.contains("icmp eq i32")
            && !stdout.contains("@Signal."),
        "LLVM output did not contain expected enum discriminant shape\nstdout:\n{}",
        stdout
    );

    let run = run_glagol([OsStr::new("--run-tests"), fixture.as_os_str()]);
    assert_success_stdout(
        run,
        concat!(
            "test \"enum constructor equality\" ... ok\n",
            "test \"enum local return call flow\" ... ok\n",
            "test \"enum parameter equality\" ... ok\n",
            "test \"enum match red\" ... ok\n",
            "test \"enum match multi expression arm\" ... ok\n",
            "test \"enum match green\" ... ok\n",
            "6 test(s) passed\n",
        ),
    );
}

#[test]
fn user_adts_alpha_formatter_and_lowering_are_visible() {
    let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/enum-basic.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 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-basic.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-basic.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);
}
