use super::*;

#[test]
fn long_options_may_not_be_empty() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .stderr(
      "
        error: option name for parameter `bar` is empty
         ——▶ justfile:1:18
          │
        1 │ [arg('bar', long='')]
          │                  ^^
      ",
    )
    .failure();
}

#[test]
fn short_options_may_not_be_empty() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .stderr(
      "
        error: option name for parameter `bar` is empty
         ——▶ justfile:1:19
          │
        1 │ [arg('bar', short='')]
          │                   ^^
      ",
    )
    .failure();
}

#[test]
fn short_options_may_not_have_multiple_characters() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='abc')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .stderr(
      "
        error: short option name for parameter `bar` contains multiple characters
         ——▶ justfile:1:19
          │
        1 │ [arg('bar', short='abc')]
          │                   ^^^^^
      ",
    )
    .failure();
}

#[test]
fn parameters_may_be_passed_with_long_options() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .args(["foo", "--bar", "baz"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn long_option_defaults_to_parameter_name() {
  Test::new()
    .justfile(
      "
        [arg('bar', long)]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .args(["foo", "--bar", "baz"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn parameters_may_be_passed_with_short_options() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .args(["foo", "-b", "baz"])
    .stdout("bar=baz\n")
    .success();
}

const LONG_SHORT: &str = "
  [arg('bar', long='bar', short='b')]
  @foo bar:
    echo bar={{bar}}
";

#[test]
fn parameters_with_both_long_and_short_option_may_be_passed_as_long() {
  Test::new()
    .justfile(LONG_SHORT)
    .args(["foo", "--bar", "baz"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn parameters_with_both_long_and_short_option_may_be_passed_as_short() {
  Test::new()
    .justfile(LONG_SHORT)
    .args(["foo", "-b", "baz"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn parameters_with_both_long_and_short_may_not_use_both() {
  Test::new()
    .justfile(LONG_SHORT)
    .args(["foo", "--bar", "baz", "-b", "baz"])
    .stderr("error: recipe `foo` option `-b` cannot be passed more than once\n")
    .failure();
}

#[test]
fn multiple_short_options_in_one_argument_is_an_error() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='a')]
        [arg('baz', short='b')]
        @foo bar baz:
      ",
    )
    .args(["foo", "-ab"])
    .stderr("error: passing multiple short options (`-ab`) in one argument is not supported\n")
    .failure();
}

#[test]
fn duplicate_long_option_attributes_are_forbidden() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        [arg('baz', long='bar')]
        foo bar baz:
      ",
    )
    .stderr(
      "
        error: recipe `foo` defines option `--bar` multiple times
         ——▶ justfile:2:18
          │
        2 │ [arg('baz', long='bar')]
          │                  ^^^^^
      ",
    )
    .failure();
}

#[test]
fn defaulted_duplicate_long_option() {
  Test::new()
    .justfile(
      "
        [arg(
          'aaa',
          long='bar'
        )]
        [arg(      'bar', long)]
        foo aaa bar:
      ",
    )
    .stderr(
      "
        error: recipe `foo` defines option `--bar` multiple times
         ——▶ justfile:5:19
          │
        5 │ [arg(      'bar', long)]
          │                   ^^^^
      ",
    )
    .failure();
}

#[test]
fn duplicate_short_option_attributes_are_forbidden() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b')]
        [arg('baz', short='b')]
        foo bar baz:
      ",
    )
    .stderr(
      "
        error: recipe `foo` defines option `-b` multiple times
         ——▶ justfile:2:19
          │
        2 │ [arg('baz', short='b')]
          │                   ^^^
      ",
    )
    .failure();
}

#[test]
fn variadics_with_long_options_are_forbidden() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        foo +bar:
      ",
    )
    .stderr(
      "
        error: variadic parameters may not be options
         ——▶ justfile:2:6
          │
        2 │ foo +bar:
          │      ^^^
      ",
    )
    .failure();
}

#[test]
fn variadics_with_short_options_are_forbidden() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b')]
        foo +bar:
      ",
    )
    .stderr(
      "
        error: variadic parameters may not be options
         ——▶ justfile:2:6
          │
        2 │ foo +bar:
          │      ^^^
      ",
    )
    .failure();
}

#[test]
fn long_option_names_may_not_contain_equal_sign() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar=baz')]
        foo bar:
      ",
    )
    .stderr(
      "
        error: option name for parameter `bar` contains equal sign
         ——▶ justfile:1:18
          │
        1 │ [arg('bar', long='bar=baz')]
          │                  ^^^^^^^^^
      ",
    )
    .failure();
}

#[test]
fn short_option_names_may_not_contain_equal_sign() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='=')]
        foo bar:
      ",
    )
    .stderr(
      "
        error: option name for parameter `bar` contains equal sign
         ——▶ justfile:1:19
          │
        1 │ [arg('bar', short='=')]
          │                   ^^^
      ",
    )
    .failure();
}

#[test]
fn long_options_may_follow_an_omitted_positional_argument() {
  Test::new()
    .justfile(
      "
        [arg('baz', long='baz')]
        @foo bar='BAR' baz:
          echo bar={{bar}}
          echo baz={{baz}}
      ",
    )
    .args(["foo", "--baz", "BAZ"])
    .stdout(
      "
        bar=BAR
        baz=BAZ
      ",
    )
    .success();
}

#[test]
fn short_options_may_follow_an_omitted_positional_argument() {
  Test::new()
    .justfile(
      "
        [arg('baz', short='b')]
        @foo bar='BAR' baz:
          echo bar={{bar}}
          echo baz={{baz}}
      ",
    )
    .args(["foo", "-b", "BAZ"])
    .stdout(
      "
        bar=BAR
        baz=BAZ
      ",
    )
    .success();
}

#[test]
fn options_with_a_default_may_be_omitted() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar='BAR':
          echo bar={{bar}}
      ",
    )
    .args(["foo"])
    .stdout(
      "
        bar=BAR
      ",
    )
    .success();
}

#[test]
fn variadics_can_follow_options() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar *baz:
          echo bar={{bar}}
          echo baz={{baz}}
      ",
    )
    .args(["foo", "--bar=BAR", "A", "B", "C"])
    .stdout(
      "
        bar=BAR
        baz=A B C
      ",
    )
    .success();
}

#[test]
fn argument_values_starting_with_dashes_are_accepted_if_recipe_does_not_take_options() {
  Test::new()
    .justfile(
      "
        @foo *baz:
          echo baz={{baz}}
      ",
    )
    .args(["foo", "--bar=BAR", "--A", "--B", "--C"])
    .stdout(
      "
        baz=--bar=BAR --A --B --C
      ",
    )
    .success();
}

#[test]
fn argument_values_starting_with_dashes_are_an_error_if_recipe_takes_options() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar *baz:
          echo bar={{bar}}
          echo baz={{baz}}
      ",
    )
    .args(["foo", "--bar=BAR", "--A", "--B", "--C"])
    .stderr("error: recipe `foo` does not have option `--A`\n")
    .failure();
}

#[test]
fn argument_values_starting_with_dashes_are_accepted_after_double_dash() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar *baz:
          echo bar={{bar}}
          echo baz={{baz}}
      ",
    )
    .args(["foo", "--bar=BAR", "--", "--A", "--B", "--C"])
    .stdout(
      "
        bar=BAR
        baz=--A --B --C
      ",
    )
    .success();
}

#[test]
fn positional_and_long_option_arguments_can_be_intermixed() {
  Test::new()
    .justfile(
      "
        [arg('b', long='b')]
        [arg('d', long='d')]
        @foo a b c d e:
          echo a={{a}}
          echo b={{b}}
          echo c={{c}}
          echo d={{d}}
          echo e={{e}}
      ",
    )
    .args(["foo", "A", "--d", "D", "C", "--b", "B", "E"])
    .stdout(
      "
        a=A
        b=B
        c=C
        d=D
        e=E
      ",
    )
    .success();
}

#[test]
fn positional_and_short_option_arguments_can_be_intermixed() {
  Test::new()
    .justfile(
      "
        [arg('b', short='b')]
        [arg('d', short='d')]
        @foo a b c d e:
          echo a={{a}}
          echo b={{b}}
          echo c={{c}}
          echo d={{d}}
          echo e={{e}}
      ",
    )
    .args(["foo", "A", "-d", "D", "C", "-b", "B", "E"])
    .stdout(
      "
        a=A
        b=B
        c=C
        d=D
        e=E
      ",
    )
    .success();
}

#[test]
fn unknown_options_are_an_error() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar:
      ",
    )
    .args(["foo", "--baz", "BAZ"])
    .stderr("error: recipe `foo` does not have option `--baz`\n")
    .failure();
}

#[test]
fn missing_required_options_are_an_error() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar:
      ",
    )
    .arg("foo")
    .stderr("error: recipe `foo` requires option `--bar`\n")
    .failure();
}

#[test]
fn duplicate_long_options_are_an_error() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar:
      ",
    )
    .args(["foo", "--bar=a", "--bar=b"])
    .stderr("error: recipe `foo` option `--bar` cannot be passed more than once\n")
    .failure();
}

#[test]
fn duplicate_short_options_are_an_error() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b')]
        @foo bar:
      ",
    )
    .args(["foo", "-b=a", "-b=b"])
    .stderr("error: recipe `foo` option `-b` cannot be passed more than once\n")
    .failure();
}

#[test]
fn options_require_value() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar:
      ",
    )
    .args(["foo", "--bar"])
    .stderr("error: recipe `foo` option `--bar` missing value\n")
    .failure();
}

#[test]
fn recipes_with_long_options_have_correct_positional_argument_mismatch_message() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar')]
        @foo bar baz:
      ",
    )
    .args(["foo", "--bar=value"])
    .stderr(
      "
        error: recipe `foo` got 0 positional arguments but takes 1
        usage:
            just foo [OPTIONS] baz
      ",
    )
    .failure();
}

#[test]
fn recipes_with_short_options_have_correct_positional_argument_mismatch_message() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b')]
        @foo bar baz:
      ",
    )
    .args(["foo", "-b=value"])
    .stderr(
      "
        error: recipe `foo` got 0 positional arguments but takes 1
        usage:
            just foo [OPTIONS] baz
      ",
    )
    .failure();
}

#[test]
fn long_options_with_values_are_flags() {
  Test::new()
    .justfile(
      "
        [arg('bar', long='bar', value='baz')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .args(["foo", "--bar"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn short_options_with_values_are_flags() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b', value='baz')]
        @foo bar:
          echo bar={{bar}}
      ",
    )
    .args(["foo", "-b"])
    .stdout("bar=baz\n")
    .success();
}

#[test]
fn flags_cannot_take_values() {
  Test::new()
    .justfile(
      "
        [arg('bar', short='b', value='baz')]
        @foo bar:
      ",
    )
    .args(["foo", "-b=hello"])
    .stderr("error: recipe `foo` flag `-b` does not take value\n")
    .failure();
}

#[test]
fn value_requires_long_or_short() {
  Test::new()
    .justfile(
      "
        [arg('bar', value='baz')]
        @foo bar:
      ",
    )
    .args(["foo", "-b=hello"])
    .stderr(
      "
        error: argument attribute `value` only valid with `long` or `short`
         ——▶ justfile:1:13
          │
        1 │ [arg('bar', value='baz')]
          │             ^^^^^
      ",
    )
    .failure();
}

#[test]
fn options_arg_passed_as_positional_arguments() {
  Test::new()
    .justfile(
      r#"
        set positional-arguments

        [arg('bar', short='b')]
        @foo bar:
          echo args="$@"
      "#,
    )
    .args(["foo", "-b", "baz"])
    .stdout("args=baz\n")
    .success();
}
