package confext

import (
	"gogs.mikescher.com/BlackForestBytes/goext/timeext"
	"gogs.mikescher.com/BlackForestBytes/goext/tst"
	"testing"
	"time"
)

func TestApplyEnvOverridesNoop(t *testing.T) {

	type aliasint int
	type aliasstring string

	type testdata struct {
		V1 int           `env:"TEST_V1"`
		VX string        ``
		V2 string        `env:"TEST_V2"`
		V3 int8          `env:"TEST_V3"`
		V4 int32         `env:"TEST_V4"`
		V5 int64         `env:"TEST_V5"`
		V6 aliasint      `env:"TEST_V6"`
		VY aliasint      ``
		V7 aliasstring   `env:"TEST_V7"`
		V8 time.Duration `env:"TEST_V8"`
		V9 time.Time     `env:"TEST_V9"`
	}

	input := testdata{
		V1: 1,
		VX: "X",
		V2: "2",
		V3: 3,
		V4: 4,
		V5: 5,
		V6: 6,
		VY: 99,
		V7: "7",
		V8: 9,
		V9: time.Unix(1671102873, 0),
	}

	output := input

	err := ApplyEnvOverrides("", &output, ".")
	if err != nil {
		t.Errorf("%v", err)
		t.FailNow()
	}

	tst.AssertEqual(t, input, output)
}

func TestApplyEnvOverridesSimple(t *testing.T) {

	type aliasint int
	type aliasstring string

	type testdata struct {
		V1 int           `env:"TEST_V1"`
		VX string        ``
		V2 string        `env:"TEST_V2"`
		V3 int8          `env:"TEST_V3"`
		V4 int32         `env:"TEST_V4"`
		V5 int64         `env:"TEST_V5"`
		V6 aliasint      `env:"TEST_V6"`
		VY aliasint      ``
		V7 aliasstring   `env:"TEST_V7"`
		V8 time.Duration `env:"TEST_V8"`
		V9 time.Time     `env:"TEST_V9"`
		VA bool          `env:"TEST_VA"`
	}

	data := testdata{
		V1: 1,
		VX: "X",
		V2: "2",
		V3: 3,
		V4: 4,
		V5: 5,
		V6: 6,
		VY: 99,
		V7: "7",
		V8: 9,
		V9: time.Unix(1671102873, 0),
		VA: false,
	}

	t.Setenv("TEST_V1", "846")
	t.Setenv("TEST_V2", "hello_world")
	t.Setenv("TEST_V3", "6")
	t.Setenv("TEST_V4", "333")
	t.Setenv("TEST_V5", "-937")
	t.Setenv("TEST_V6", "070")
	t.Setenv("TEST_V7", "AAAAAA")
	t.Setenv("TEST_V8", "1min4s")
	t.Setenv("TEST_V9", "2009-11-10T23:00:00Z")
	t.Setenv("TEST_VA", "true")

	err := ApplyEnvOverrides("", &data, ".")
	if err != nil {
		t.Errorf("%v", err)
		t.FailNow()
	}

	tst.AssertEqual(t, data.V1, 846)
	tst.AssertEqual(t, data.V2, "hello_world")
	tst.AssertEqual(t, data.V3, 6)
	tst.AssertEqual(t, data.V4, 333)
	tst.AssertEqual(t, data.V5, -937)
	tst.AssertEqual(t, data.V6, 70)
	tst.AssertEqual(t, data.V7, "AAAAAA")
	tst.AssertEqual(t, data.V8, time.Second*64)
	tst.AssertEqual(t, data.V9, time.Unix(1257894000, 0).UTC())
	tst.AssertEqual(t, data.VA, true)
}

func TestApplyEnvOverridesRecursive(t *testing.T) {

	type subdata struct {
		V1 int           `env:"SUB_V1"`
		VX string        ``
		V2 string        `env:"SUB_V2"`
		V8 time.Duration `env:"SUB_V3"`
		V9 time.Time     `env:"SUB_V4"`
	}

	type testdata struct {
		V1   int     `env:"TEST_V1"`
		VX   string  ``
		Sub1 subdata ``
		Sub2 subdata `env:"TEST_V2"`
		Sub3 subdata `env:"TEST_V3"`
		Sub4 subdata `env:""`
		V5   string  `env:"-"`
	}

	data := testdata{
		V1: 1,
		VX: "2",
		V5: "no",
		Sub1: subdata{
			V1: 3,
			VX: "4",
			V2: "5",
			V8: 6 * time.Second,
			V9: time.Date(2000, 1, 7, 1, 1, 1, 0, time.UTC),
		},
		Sub2: subdata{
			V1: 8,
			VX: "9",
			V2: "10",
			V8: 11 * time.Second,
			V9: time.Date(2000, 1, 12, 1, 1, 1, 0, timeext.TimezoneBerlin),
		},
		Sub3: subdata{
			V1: 13,
			VX: "14",
			V2: "15",
			V8: 16 * time.Second,
			V9: time.Date(2000, 1, 17, 1, 1, 1, 0, timeext.TimezoneBerlin),
		},
		Sub4: subdata{
			V1: 18,
			VX: "19",
			V2: "20",
			V8: 21 * time.Second,
			V9: time.Date(2000, 1, 22, 1, 1, 1, 0, timeext.TimezoneBerlin),
		},
	}

	t.Setenv("TEST_V1", "999")
	t.Setenv("-", "yes")

	t.Setenv("TEST_V2_SUB_V1", "846")
	t.Setenv("TEST_V2_SUB_V2", "222_hello_world")
	t.Setenv("TEST_V2_SUB_V3", "1min4s")
	t.Setenv("TEST_V2_SUB_V4", "2009-11-10T23:00:00Z")

	t.Setenv("TEST_V3_SUB_V1", "33846")
	t.Setenv("TEST_V3_SUB_V2", "33_hello_world")
	t.Setenv("TEST_V3_SUB_V3", "33min4s")
	t.Setenv("TEST_V3_SUB_V4", "2033-11-10T23:00:00Z")

	t.Setenv("SUB_V1", "11")
	t.Setenv("SUB_V2", "22")
	t.Setenv("SUB_V3", "33min")
	t.Setenv("SUB_V4", "2044-01-01T00:00:00Z")

	err := ApplyEnvOverrides("", &data, "_")
	if err != nil {
		t.Errorf("%v", err)
		t.FailNow()
	}

	tst.AssertEqual(t, data.V1, 999)
	tst.AssertEqual(t, data.VX, "2")
	tst.AssertEqual(t, data.V5, "no")
	tst.AssertEqual(t, data.Sub1.V1, 3)
	tst.AssertEqual(t, data.Sub1.VX, "4")
	tst.AssertEqual(t, data.Sub1.V2, "5")
	tst.AssertEqual(t, data.Sub1.V8, time.Second*6)
	tst.AssertEqual(t, data.Sub1.V9, time.Unix(947206861, 0).UTC())
	tst.AssertEqual(t, data.Sub2.V1, 846)
	tst.AssertEqual(t, data.Sub2.VX, "9")
	tst.AssertEqual(t, data.Sub2.V2, "222_hello_world")
	tst.AssertEqual(t, data.Sub2.V8, time.Second*64)
	tst.AssertEqual(t, data.Sub2.V9, time.Unix(1257894000, 0).UTC())
	tst.AssertEqual(t, data.Sub3.V1, 33846)
	tst.AssertEqual(t, data.Sub3.VX, "14")
	tst.AssertEqual(t, data.Sub3.V2, "33_hello_world")
	tst.AssertEqual(t, data.Sub3.V8, time.Second*1984)
	tst.AssertEqual(t, data.Sub3.V9, time.Unix(2015276400, 0).UTC())
	tst.AssertEqual(t, data.Sub4.V1, 11)
	tst.AssertEqual(t, data.Sub4.VX, "19")
	tst.AssertEqual(t, data.Sub4.V2, "22")
	tst.AssertEqual(t, data.Sub4.V8, time.Second*1980)
	tst.AssertEqual(t, data.Sub4.V9, time.Unix(2335219200, 0).UTC())
}

func TestApplyEnvOverridesPointer(t *testing.T) {

	type aliasint int
	type aliasstring string

	type testdata struct {
		V1 *int           `env:"TEST_V1"`
		VX *string        ``
		V2 *string        `env:"TEST_V2"`
		V3 *int8          `env:"TEST_V3"`
		V4 *int32         `env:"TEST_V4"`
		V5 *int64         `env:"TEST_V5"`
		V6 *aliasint      `env:"TEST_V6"`
		VY *aliasint      ``
		V7 *aliasstring   `env:"TEST_V7"`
		V8 *time.Duration `env:"TEST_V8"`
		V9 *time.Time     `env:"TEST_V9"`
	}

	data := testdata{}

	t.Setenv("TEST_V1", "846")
	t.Setenv("TEST_V2", "hello_world")
	t.Setenv("TEST_V3", "6")
	t.Setenv("TEST_V4", "333")
	t.Setenv("TEST_V5", "-937")
	t.Setenv("TEST_V6", "070")
	t.Setenv("TEST_V7", "AAAAAA")
	t.Setenv("TEST_V8", "1min4s")
	t.Setenv("TEST_V9", "2009-11-10T23:00:00Z")

	err := ApplyEnvOverrides("", &data, ".")
	if err != nil {
		t.Errorf("%v", err)
		t.FailNow()
	}

	tst.AssertDeRefEqual(t, data.V1, 846)
	tst.AssertDeRefEqual(t, data.V2, "hello_world")
	tst.AssertDeRefEqual(t, data.V3, 6)
	tst.AssertDeRefEqual(t, data.V4, 333)
	tst.AssertDeRefEqual(t, data.V5, -937)
	tst.AssertDeRefEqual(t, data.V6, 70)
	tst.AssertDeRefEqual(t, data.V7, "AAAAAA")
	tst.AssertDeRefEqual(t, data.V8, time.Second*64)
	tst.AssertDeRefEqual(t, data.V9, time.Unix(1257894000, 0).UTC())
}

func assertEqual[T comparable](t *testing.T, actual T, expected T) {
	if actual != expected {
		t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
	}
}

func assertPtrEqual[T comparable](t *testing.T, actual *T, expected T) {
	if actual == nil {
		t.Errorf("values differ: Actual: NIL, Expected: '%v'", expected)
	}
	if *actual != expected {
		t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
	}
}
