package test

import (
	"testing"

	. "github.com/onsi/gomega"
)

func TestCheckNonZeroValue(t *testing.T) {
	g := NewGomegaWithT(t)

	type A struct {
		Bool bool
		Int  int
	}

	type B struct {
		Bool bool
		A    A
	}

	type C struct {
		AP *A
	}

	var obj *A
	var boolP *bool
	var intP *int
	var strP *string
	var ints []int
	var strm map[string]string

	expectErr := func(result error) {
		g.Expect(result).To(HaveOccurred())
		g.Expect(result.Error()).To(ContainSubstring("is zero value"))
	}

	cases := map[string]struct {
		objs          []any
		ignoredFields []string
		expect        func(result error)
	}{
		"obj is int but zero value": {
			objs:   []any{0, uint64(0), int64(0), int32(0), int16(0), int8(0), uint32(0), uint16(0), uint8(0)},
			expect: expectErr,
		},
		"obj is string but zero value": {
			objs:   []any{""},
			expect: expectErr,
		},
		"obj is bool but false": {
			objs:   []any{false},
			expect: expectErr,
		},
		"obj is map/slices but empty": {
			objs:   []any{[]int{}, []string{}, map[string]int{}, map[string]string{}},
			expect: expectErr,
		},
		"obj is map/slices but nil": {
			objs:   []any{ints, strm},
			expect: expectErr,
		},
		"obj is pointer but nil": {
			objs:   []any{boolP, intP, strP, obj},
			expect: expectErr,
		},
		"obj is struct but a field is zero value": {
			objs:   []any{A{true, 0}, A{false, 1}},
			expect: expectErr,
		},
		"obj is struct but a sub field is zero value": {
			objs:   []any{B{true, A{true, 0}}, B{true, A{false, 1}}},
			expect: expectErr,
		},
		"obj has pointer field": {
			objs:   []any{C{}, C{&A{true, 0}}},
			expect: expectErr,
		},
		"obj is struct and non zero value": {
			objs: []any{A{true, 1}, B{true, A{true, 1}}},
			expect: func(result error) {
				g.Expect(result).To(BeNil())
			},
		},
		"obj has zero value field but ignored": {
			objs:          []any{B{false, A{true, 1}}, B{true, A{true, 0}}, B{true, A{false, 1}}},
			ignoredFields: []string{".Bool", ".A.Int", ".A.Bool"},
			expect: func(result error) {
				g.Expect(result).To(BeNil())
			},
		},
	}

	for name, c := range cases {
		t.Logf("Test case: %s", name)
		for _, obj := range c.objs {
			err := CheckNonZeroValue(obj, c.ignoredFields)
			c.expect(err)
		}
	}

}
