package config

import (
	"log"
	"os"
	"reflect"
	"testing"
	"time"
)

func TestParseStruct(t *testing.T) {
	c, err := NewConfig()
	if err != nil {
		t.Fatal(err)
	}

	test1 := new(map[string]interface{})
	if err := c.parseStruct(test1); err == nil {
		t.Error("Should have errored when loading into a non-struct.")
	}

	v := struct {
		Port   int
		Host   string
		Random int `forerunner:"blah,secret"`
	}{}

	if err := c.parseStruct(&v); err != nil {
		t.Errorf("fr.LoadFlags(&v) got %v; want nil", err)
	}
}

func TestGetName(t *testing.T) {
	c := struct {
		Host   string
		Random int `forerunner:"blah,secret"`
	}{}

	st := reflect.TypeOf(c)

	if name := getName(st.Field(0)); name != "host" {
		t.Errorf("getName(Host) got: %q; want host", name)
	}

	if name := getName(st.Field(1)); name != "blah" {
		t.Errorf("getName(Random) got: %q; want blah", name)
	}
}

func TestGetDescription(t *testing.T) {
	c := struct {
		Host            string
		Random          int    `description:"hello world"`
		ComplicatedDesc string `description:"This one contains \"escaped\" quotes"`
	}{}

	st := reflect.TypeOf(c)

	cases := []string{
		"",
		"hello world",
		`This one contains "escaped" quotes`,
	}

	for i, desc := range cases {
		if out := getDescription(st.Field(i)); out != desc {
			t.Errorf(`getDescription(%s) got: %q; want %q`, st.Field(i).Name, out, desc)
		}
	}
}

func TestUnmarshal(t *testing.T) {
	// Use time as a method for testing structs:
	testTime := time.Date(1990, time.August, 22, 12, 05, 00, 0, time.UTC)

	cases := []struct {
		in  interface{}
		res interface{}
	}{
		// Test Ints:
		{in: 80, res: 80},
		{in: "80", res: 80},

		// Test floats:
		{in: 80.5, res: 80.5},
		{in: "80.5", res: 80.5},

		// Test strings:
		{in: "foo bar", res: "foo bar"},
		{in: "80", res: "80"},

		{in: true, res: true},
		{in: "true", res: true},

		// Test structs:
		{in: testTime.Format(time.RFC3339), res: testTime.Format(time.RFC3339)},
		// time.Time implements encoding.TextUnmarshaler:
		{in: testTime.Format(time.RFC3339), res: testTime},
	}

	for i, c := range cases {
		resValue := reflect.ValueOf(c.res)
		ptr := reflect.New(resValue.Type()).Interface()

		if err := unmarshal(c.in, ptr); err != nil {
			t.Errorf("Case[%d] Error unmarshalling %v => %v: %v", i, c.in, c.res, err)
			continue
		}

		ptrContent := reflect.Indirect(reflect.ValueOf(ptr))
		if c.res != ptrContent.Interface() {
			t.Errorf("Case[%d] unmarshal(%v, ptr) = %v; want %v", i, c.in, ptrContent, c.res)
		}
	}
}

func TestGetSecret(t *testing.T) {
	type testStruct struct {
		Field1 string
		Field2 string `forerunner:",secret"`
	}

	ts := testStruct{"a", "b"}

	typeTS := reflect.TypeOf(ts)
	field1 := typeTS.Field(0)
	field2 := typeTS.Field(1)

	if secretField(field1) {
		t.Errorf("secretField(field1) = true; want false")
	}

	if !secretField(field2) {
		t.Errorf("secretField(field2) = false; want true")
	}
}

func ExamplePrint() {
	type testStruct struct {
		Field1 int
		Field2 string
		Field3 string `forerunner:",secret"`
	}

	c, err := NewConfig()
	if err != nil {
		log.Fatal(err)
	}

	if err := os.Setenv("FIELD1", "2"); err != nil {
		log.Fatal(err)
	}

	ts := &testStruct{1, "abc", "hunter2"}
	c.SetDefaults(ts)
	c.GetConfig(ts, nil)

	c.Print()
	// Output:
	// Field1 [env]: 2
	// Field2 [default]: abc
	// Field3 [default]: **********
}
