use std::{error::Error, process::Command};

use executable_path::executable_path;
use test_utilities::tmptree;

const JUSTFILE: &str = r#"
foo := `cat data`

linewise bar=`cat data`: shebang
  echo expression: {{foo}}
  echo default: {{bar}}
  echo linewise: `cat data`

shebang:
  #!/usr/bin/env sh
  echo "shebang:" `cat data`
"#;

const DATA: &str = "OK";

const WANT: &str = "shebang: OK\nexpression: OK\ndefault: OK\nlinewise: OK\n";

/// Test that just runs with the correct working directory when invoked with
/// `--justfile` but not `--working-directory`
#[test]
fn justfile_without_working_directory() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      justfile: JUSTFILE,
      data: DATA,
    };

    let output = Command::new(executable_path("just"))
        .arg("--justfile")
        .arg(&tmp.path().join("justfile"))
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8(output.stdout).unwrap();

    assert_eq!(stdout, WANT);

    Ok(())
}

/// Test that just runs with the correct working directory when invoked with
/// `--justfile` but not `--working-directory`, and justfile path has no parent
#[test]
fn justfile_without_working_directory_relative() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      justfile: JUSTFILE,
      data: DATA,
    };

    let output = Command::new(executable_path("just"))
        .current_dir(&tmp.path())
        .arg("--justfile")
        .arg("justfile")
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8(output.stdout).unwrap();

    assert_eq!(stdout, WANT);

    Ok(())
}

/// Test that just invokes commands from the directory in which the justfile is
/// found
#[test]
fn change_working_directory_to_search_justfile_parent() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      justfile: JUSTFILE,
      data: DATA,
      subdir: {},
    };

    let output = Command::new(executable_path("just"))
        .current_dir(tmp.path().join("subdir"))
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8_lossy(&output.stdout);

    assert_eq!(stdout, WANT);

    Ok(())
}

/// Test that just runs with the correct working directory when invoked with
/// `--justfile` but not `--working-directory`
#[test]
fn justfile_and_working_directory() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      justfile: JUSTFILE,
      sub: {
        data: DATA,
      },
    };

    let output = Command::new(executable_path("just"))
        .arg("--justfile")
        .arg(&tmp.path().join("justfile"))
        .arg("--working-directory")
        .arg(&tmp.path().join("sub"))
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8(output.stdout).unwrap();

    assert_eq!(stdout, WANT);

    Ok(())
}

/// Test that just runs with the correct working directory when invoked with
/// `--justfile` but not `--working-directory`
#[test]
fn search_dir_child() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      child: {
        justfile: JUSTFILE,
        data: DATA,
      },
    };

    let output = Command::new(executable_path("just"))
        .current_dir(&tmp.path())
        .arg("child/")
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8(output.stdout).unwrap();

    assert_eq!(stdout, WANT);

    Ok(())
}

/// Test that just runs with the correct working directory when invoked with
/// `--justfile` but not `--working-directory`
#[test]
fn search_dir_parent() -> Result<(), Box<dyn Error>> {
    let tmp = tmptree! {
      child: {
      },
      justfile: JUSTFILE,
      data: DATA,
    };

    let output = Command::new(executable_path("just"))
        .current_dir(&tmp.path().join("child"))
        .arg("../")
        .output()?;

    if !output.status.success() {
        eprintln!("{:?}", String::from_utf8_lossy(&output.stderr));
        panic!();
    }

    let stdout = String::from_utf8(output.stdout).unwrap();

    assert_eq!(stdout, WANT);

    Ok(())
}
