package schema

import (
	"reflect"
	"testing"
)

func TestParseTagAndOptions(t *testing.T) {
	alias, opts := parseTag("name,omitempty,default:foo")
	if alias != "name" {
		t.Fatalf("expected alias name, got %s", alias)
	}
	if !opts.Contains("omitempty") {
		t.Fatalf("expected omitempty option")
	}
	if val := opts.getDefaultOptionValue(); val != "foo" {
		t.Fatalf("expected default foo, got %s", val)
	}
}

func TestFieldAlias(t *testing.T) {
	type S struct {
		Field string `json:"custom,omitempty"`
	}
	f, ok := reflect.TypeOf(S{}).FieldByName("Field")
	if !ok {
		t.Fatal("field not found")
	}
	alias, opts := fieldAlias(f, "json")
	if alias != "custom" {
		t.Fatalf("expected alias custom, got %s", alias)
	}
	if !opts.Contains("omitempty") {
		t.Fatalf("expected omitempty option")
	}
}

func TestTagOptionsContains(t *testing.T) {
	opts := tagOptions("a,b,default:val")
	if !opts.Contains("a") || opts.Contains("c") {
		t.Fatalf("contains failed")
	}
	if val := opts.getDefaultOptionValue(); val != "val" {
		t.Fatalf("expected default val, got %s", val)
	}
}

func BenchmarkParseTag(b *testing.B) {
	for b.Loop() {
		parseTag("field,omitempty,default:value")
	}
}

func BenchmarkIsZero(b *testing.B) {
	type S struct{ A int }
	v := reflect.ValueOf(S{})
	for b.Loop() {
		isZero(v)
	}
}

type customZero struct{ A int }

func (c customZero) IsZero() bool { return c.A == 0 }

func TestIsZeroCases(t *testing.T) {
	var sl []int
	if !isZero(reflect.ValueOf(sl)) {
		t.Errorf("nil slice should be zero")
	}
	sl = []int{}
	if !isZero(reflect.ValueOf(sl)) {
		t.Errorf("empty slice should be zero")
	}
	sl = []int{1}
	if isZero(reflect.ValueOf(sl)) {
		t.Errorf("non-empty slice considered zero")
	}

	arr := [2]int{}
	if !isZero(reflect.ValueOf(arr)) {
		t.Errorf("zero array should be zero")
	}
	arr = [2]int{0, 1}
	if isZero(reflect.ValueOf(arr)) {
		t.Errorf("non-zero array considered zero")
	}

	type S struct {
		A int
		B string
	}
	if !isZero(reflect.ValueOf(S{})) {
		t.Errorf("zero struct should be zero")
	}
	if isZero(reflect.ValueOf(S{A: 1})) {
		t.Errorf("non-zero struct considered zero")
	}

	if !isZero(reflect.ValueOf(customZero{})) {
		t.Errorf("IsZero method not used for zero value")
	}
	if isZero(reflect.ValueOf(customZero{A: 1})) {
		t.Errorf("IsZero method not used for non-zero value")
	}
}

func TestIsZeroFuncAndMap(t *testing.T) {
	// nil func should be zero
	var nilFn func()
	if !isZero(reflect.ValueOf(nilFn)) {
		t.Errorf("nil func should be zero")
	}
	// non-nil func should not be zero
	nonNilFn := func() {}
	if isZero(reflect.ValueOf(nonNilFn)) {
		t.Errorf("non-nil func should not be zero")
	}

	var m map[string]int
	if !isZero(reflect.ValueOf(m)) {
		t.Errorf("nil map should be zero")
	}
	m = map[string]int{}
	if !isZero(reflect.ValueOf(m)) {
		t.Errorf("empty map should be zero")
	}
	m["a"] = 1
	if isZero(reflect.ValueOf(m)) {
		t.Errorf("non-empty map considered zero")
	}
}
