package wand

import "testing"

func TestInput(t *testing.T) {
	t.Run("keyvals", func(t *testing.T) {
		type s0 struct {
			Foo string
			Bar int
		}

		f0 := func(a s0) string { return a.Foo }
		t.Run(
			"ignore if not defined",
			testExec(
				testCase{
					impl:    f0,
					conf:    "bar=42",
					command: "foo",
				},
				"",
				"",
			),
		)

		t.Run(
			"multiple values for field that does not allow lists",
			testExec(
				testCase{
					impl:    f0,
					conf:    "foo=42\nfoo=baz",
					command: "foo",
				},
				"expected only one value",
				"",
			),
		)

		t.Run(
			"unscannable value",
			testExec(
				testCase{
					impl:    f0,
					conf:    "bar=baz",
					command: "foo",
				},
				"type mismatch",
				"",
			),
		)
	})

	t.Run("options", func(t *testing.T) {
		type s0 struct {
			Foo string
			Bar int
		}

		f0 := func(a s0) string { return a.Foo }
		t.Run(
			"undefined short form",
			testExec(testCase{impl: f0, command: "foo -f"}, "option not supported", ""),
		)

		t.Run(
			"undefined option",
			testExec(testCase{impl: f0, command: "foo --baz"}, "option not supported", ""),
		)

		t.Run(
			"multiple values for field that does not allow lists",
			testExec(testCase{impl: f0, command: "foo --foo bar --foo baz"}, "expected only one value", ""),
		)

		t.Run(
			"bool value for field that does not accept it",
			testExec(testCase{impl: f0, command: "foo --foo"}, "received boolean value", ""),
		)

		t.Run(
			"cannot scan",
			testExec(testCase{impl: f0, command: "foo --bar baz"}, "type mismatch", ""),
		)
	})

	t.Run("positional args", func(t *testing.T) {
		f := func(a, b, c int) int { return a + b + c }

		var fv func(int, int, ...int) int
		fv = func(a, b int, c ...int) int {
			if len(c) == 0 {
				return a + b
			}

			if len(c) == 1 {
				return a + b + c[0]
			}

			return a + b + fv(c[0], c[1], c[2:]...)
		}

		t.Run("min max ok", testExec(testCase{impl: f, command: "foo 1 2 3"}, "", "6"))
		t.Run(
			"min missed",
			testExec(testCase{impl: f, command: "foo 1 2"}, "not enough positional arguments", ""),
		)

		t.Run(
			"max missed",
			testExec(testCase{impl: f, command: "foo 1 2 3 4"}, "too many positional arguments", ""),
		)

		t.Run(
			"min max with variadic ok",
			testExec(testCase{impl: fv, command: "foo 1 2 3 4 5"}, "", "15"),
		)

		t.Run(
			"min with variadic missed",
			testExec(testCase{impl: fv, command: "foo 1"}, "not enough positional arguments", ""),
		)

		t.Run(
			"min max with variadic and constraints ok",
			testExec(
				testCase{impl: Args(Command("foo", fv), 3, 5), command: "foo 1 2 3 4"},
				"",
				"10",
			),
		)

		t.Run(
			"min with variadic and constraints missed",
			testExec(
				testCase{impl: Args(Command("foo", fv), 3, 5), command: "foo 1 2"},
				"not enough positional arguments",
				"",
			),
		)

		t.Run(
			"max with variadic and constraints missed",
			testExec(
				testCase{impl: Args(Command("foo", fv), 3, 5), command: "foo 1 2 3 4 5 6"},
				"too many positional arguments",
				"",
			),
		)

		t.Run(
			"cannot scan",
			testExec(testCase{impl: f, command: "foo 42 bar 84"}, "cannot apply positional argument", ""),
		)
	})
}
