package wand

import (
	"fmt"
	"strings"
	"testing"
)

func TestConfig(t *testing.T) {
	type f struct{ One, SecondVar string }
	ff := func(f f) string {
		return f.One + f.SecondVar
	}

	type i struct{ One, SecondVar int }
	fi := func(i i) string {
		return fmt.Sprintf("%d;%d", i.One, i.SecondVar)
	}

	type m struct{ One, SecondVar []string }
	fm := func(m m) string {
		return strings.Join([]string{strings.Join(m.One, ","), strings.Join(m.SecondVar, ",")}, ";")
	}

	t.Run(
		"common config var casing",
		testExec(testCase{impl: ff, conf: "one=bar\nsecond_var=baz", command: "foo"}, "", "barbaz"),
	)

	t.Run(
		"camel casing",
		testExec(testCase{impl: ff, conf: "one=bar\nsecondVar=baz", command: "foo"}, "", "barbaz"),
	)

	t.Run(
		"empty var",
		testExec(testCase{impl: ff, conf: "one=bar\nsecondVar=", command: "foo"}, "", "bar"),
	)

	t.Run(
		"discard env var",
		testExec(testCase{impl: ff, conf: "one=bar\nsecondVar=baz\none", command: "foo"}, "", "baz"),
	)

	t.Run(
		"eq in value",

		// this one is a good example for fixing the error reporting in the parser. Try it by removing the
		// quotes:
		testExec(testCase{impl: ff, conf: "one=\"bar=baz\"", command: "foo"}, "", "bar=baz"),
	)

	t.Run(
		"keeps original name",
		testExec(testCase{impl: fi, conf: "ONE=bar", command: "foo"}, "ONE", ""),
	)

	t.Run(
		"keeps original name, last wins on conflict",
		testExec(testCase{impl: fi, conf: "ONE=bar\none=baz", command: "foo"}, "one", ""),
	)

	t.Run(
		"2 entries",
		testExec(testCase{impl: fm, conf: "one=bar\none=baz", command: "foo"}, "", "bar,baz;"),
	)

	t.Run(
		"3 entries",
		testExec(testCase{impl: fm, conf: "one=bar\none=baz\none=qux", command: "foo"}, "", "bar,baz,qux;"),
	)

	t.Run(
		"with empty",
		testExec(testCase{impl: fm, conf: "one=bar\none=baz\none=\none=qux\none=", command: "foo"}, "", "bar,baz,,qux,;"),
	)

	t.Run(
		"escape",
		testExec(testCase{impl: fm, conf: "one=bar\\\nbaz", command: "foo"}, "", "bar\nbaz;"),
	)

	t.Run(
		"quote",
		testExec(testCase{impl: fm, conf: "one=\"bar\nbaz\"", command: "foo"}, "", "bar\nbaz;"),
	)

	t.Run(
		"escape char",
		testExec(testCase{impl: fm, conf: "one=bar\\\\\none=baz", command: "foo"}, "", "bar\\,baz;"),
	)

	t.Run(
		"escape char last",
		testExec(testCase{impl: fm, conf: "one=bar\\", command: "foo"}, "parse failed", ""),
	)

	t.Run(
		"discard in same doc",
		testExec(testCase{impl: fm, conf: "one=bar\nsecond-var=baz\none", command: "foo"}, "", ";baz"),
	)

	t.Run(
		"discard in previous doc",
		testExec(
			testCase{
				impl:      fm,
				mergeConf: []string{"one=bar\nsecond-var=baz", "one\nsecond-var=qux"},
				command:   "foo",
			},
			"",
			";qux",
		),
	)

	t.Run(
		"discard in previous same doc",
		testExec(
			testCase{
				impl:      fm,
				mergeConf: []string{"one=bar\nsecond-var=baz", "one\nsecond-var=qux\nsecond-var"},
				command:   "foo",
			},
			"",
			";",
		),
	)

	// check the syntax for tests
	// - white space ignored
	// - comment line
	// - comment at the end of an entry
	// - invalid key
	// check the code for tests

	t.Run("white space ignored", testExec(testCase{impl: fm, conf: "one = bar ", command: "foo"}, "", "bar;"))
	t.Run(
		"comments",
		testExec(
			testCase{
				impl:    fm,
				conf:    "# comment on a line\none=bar # comment after an entry",
				command: "foo",
			},
			"",
			"bar;",
		),
	)

	t.Run("invald key", testExec(testCase{impl: fm, conf: "one two = bar", command: "foo"}, "parse failed", ""))
}
