use std::path::Path;

use assert_cmd::Command;
use predicates::boolean::PredicateBooleanExt;

fn numbat() -> Command {
    let module_path = Path::new(&std::env::var_os("CARGO_MANIFEST_DIR").unwrap())
        .parent()
        .unwrap()
        .join("numbat")
        .join("modules");
    std::env::set_var("NUMBAT_MODULES_PATH", module_path);

    let mut cmd = Command::cargo_bin("numbat").unwrap();
    cmd.arg("--no-init");
    cmd.arg("--no-config");
    cmd
}

#[test]
fn pass_expression_on_command_line() {
    numbat()
        .arg("--expression")
        .arg("2 meter + 3 meter")
        .assert()
        .success()
        .stdout(predicates::str::contains("5 m"));

    numbat()
        .arg("-e")
        .arg("let x = 2")
        .arg("-e")
        .arg("x^3")
        .assert()
        .success()
        .stdout(predicates::str::contains("8"));

    numbat()
        .arg("--expression")
        .arg("2 +/ 3")
        .assert()
        .stderr(predicates::str::contains("while parsing"));

    numbat()
        .arg("--expression")
        .arg("2 meter + 3 second")
        .assert()
        .failure()
        .stderr(predicates::str::contains("while type checking"));

    numbat()
        .arg("--expression")
        .arg("1/0")
        .assert()
        .failure()
        .stderr(predicates::str::contains("runtime error"));

    numbat()
        .arg("--expression")
        .arg("type(2 m/s)")
        .assert()
        .success()
        .stdout(predicates::str::contains("Length / Time"));
}

#[test]
fn read_code_from_file() {
    numbat()
        .arg("tests/examples/pendulum.nbt")
        .assert()
        .success();

    numbat()
        .arg("tests/examples/parser_error.nbt")
        .assert()
        .failure()
        .stderr(predicates::str::contains("while parsing"));
}

#[test]
fn process_code_from_file_and_cli_expression() {
    numbat()
        .arg("tests/examples/pendulum.nbt")
        .arg("--expression")
        .arg("oscillation_time(20cm)")
        .assert()
        .success();

    numbat()
        .arg("tests/examples/pendulum.nbt")
        .arg("--expression")
        .arg("oscillation_time(20kg)")
        .assert()
        .failure()
        .stderr(predicates::str::contains("while type checking"));
}

#[test]
fn print_calls() {
    numbat()
        .arg("tests/examples/print.nbt")
        .assert()
        .success()
        .stdout(predicates::str::contains(
            "1 \n2 m\nhello world\npi = 3.14159\n1 + 2 = 3\n",
        ));
}

#[test]
fn without_prelude() {
    numbat()
        .arg("--no-prelude")
        .arg("--expression")
        .arg("2.1 + 3.1")
        .assert()
        .success()
        .stdout(predicates::str::contains("5.2"));

    numbat()
        .arg("--no-prelude")
        .arg("--expression")
        .arg("1 meter")
        .assert()
        .failure()
        .stderr(predicates::str::contains("unknown identifier"));
}

#[test]
fn pretty_printing() {
    numbat()
        .arg("--pretty-print=always")
        .arg("--expression")
        .arg("let v=30km/h")
        .assert()
        .success()
        .stdout(predicates::str::contains(
            "let v: Velocity = 30 kilometre / hour",
        ));
}

#[test]
fn help_text() {
    numbat()
        .write_stdin("help")
        .assert()
        .success()
        .stdout(predicates::str::contains(
            "Energy of red photons: 1.87855 eV",
        ));
}

#[test]
fn info_text() {
    numbat().write_stdin("info g0").assert().success().stdout(
        predicates::str::contains("Standard acceleration of gravity on earth")
            .and(predicates::str::contains("9.80665 m/s²")),
    );

    numbat().write_stdin("info C").assert().success().stdout(
        predicates::str::contains("Coulomb").and(predicates::str::contains("1 coulomb = ")),
    );
}
