package reflect_test

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"internal/reflectlite"
	. "reflect"
	"runtime"
	"slices"
	"sort"
	"strings"
	"testing"
)

func TestTinyIndirectPointers(t *testing.T) {
	var m = map[string]int{}
	m["x"] = 1

	var a = &m

	if ValueOf(a).Elem().Len() != 1 {
		t.Errorf("bad map length via reflect")
	}

	var b struct {
		Decoded *[3]byte
	}

	v1 := New(TypeOf(b.Decoded).Elem())

	var bb [3]byte
	bb[0] = 0xaa

	v1.Elem().Set(ValueOf(bb))

	if v1.Elem().Index(0).Uint() != 0xaa {
		t.Errorf("bad indirect array index via reflect")
	}
}

func TestTinyMap(t *testing.T) {

	m := make(map[string]int)

	mtyp := TypeOf(m)

	if got, want := mtyp.Key().Kind().String(), "string"; got != want {
		t.Errorf("m.Type().Key().String()=%q, want %q", got, want)
	}

	if got, want := mtyp.Elem().Kind().String(), "int"; got != want {
		t.Errorf("m.Elem().String()=%q, want %q", got, want)
	}

	m["foo"] = 2

	mref := ValueOf(m)
	two := mref.MapIndex(ValueOf("foo"))

	if got, want := two.Interface().(int), 2; got != want {
		t.Errorf("MapIndex(`foo`)=%v, want %v", got, want)
	}

	m["bar"] = 3
	m["baz"] = 4
	m["qux"] = 5

	it := mref.MapRange()

	var gotKeys []string
	for it.Next() {
		k := it.Key()
		v := it.Value()

		kstr := k.Interface().(string)
		vint := v.Interface().(int)

		gotKeys = append(gotKeys, kstr)

		if m[kstr] != vint {
			t.Errorf("m[%v]=%v, want %v", kstr, vint, m[kstr])
		}
	}
	var wantKeys []string
	for k := range m {
		wantKeys = append(wantKeys, k)
	}
	sort.Strings(gotKeys)
	sort.Strings(wantKeys)

	if !equal(gotKeys, wantKeys) {
		t.Errorf("MapRange return unexpected keys: got %v, want %v", gotKeys, wantKeys)
	}

	refMapKeys := mref.MapKeys()
	gotKeys = gotKeys[:0]
	for _, v := range refMapKeys {
		gotKeys = append(gotKeys, v.Interface().(string))
	}

	sort.Strings(gotKeys)
	if !equal(gotKeys, wantKeys) {
		t.Errorf("MapKeys return unexpected keys: got %v, want %v", gotKeys, wantKeys)
	}

	mref.SetMapIndex(ValueOf("bar"), Value{})
	if _, ok := m["bar"]; ok {
		t.Errorf("SetMapIndex failed to delete `bar`")
	}

	mref.SetMapIndex(ValueOf("baz"), ValueOf(6))
	if got, want := m["baz"], 6; got != want {
		t.Errorf("SetMapIndex(bar, 6) got %v, want %v", got, want)
	}

	m2ref := MakeMap(mref.Type())
	m2ref.SetMapIndex(ValueOf("foo"), ValueOf(2))

	m2 := m2ref.Interface().(map[string]int)

	if m2["foo"] != 2 {
		t.Errorf("MakeMap failed to create map")
	}

	type stringint struct {
		s string
		i int
	}

	simap := make(map[stringint]int)

	refsimap := MakeMap(TypeOf(simap))

	refsimap.SetMapIndex(ValueOf(stringint{"hello", 4}), ValueOf(6))

	six := refsimap.MapIndex(ValueOf(stringint{"hello", 4}))

	if six.Interface().(int) != 6 {
		t.Errorf("m[hello, 4]=%v, want 6", six)
	}

	keys := refsimap.MapKeys()
	if len(keys) != 1 {
		t.Errorf("refsimap: MapKeys()=%v, want 1", len(keys))
	}
	if keys[0].Type() != TypeOf(stringint{}) {
		t.Errorf("keys[0] has wrong type: %v", keys[0].Type().String())
	}

	sikey := keys[0].Interface().(stringint)

	if sikey != (stringint{"hello", 4}) {
		t.Errorf("sikey has unexpected value: %#v", sikey)
	}

	// make sure we can look up interface keys with reflection
	type unmarshalerText struct {
		A, B string
	}

	ut := make(map[unmarshalerText]bool)

	refut := ValueOf(ut)

	// put in a key with the compiler
	ut[unmarshalerText{"x", "y"}] = true

	// make sure we can get it out
	v2 := refut.MapIndex(ValueOf(unmarshalerText{"x", "y"}))
	if !v2.IsValid() || !v2.Bool() {
		t.Errorf("Failed to look up map struct key with reflection")
	}

	// put in a key with reflection
	refut.SetMapIndex(ValueOf(unmarshalerText{"y", "z"}), ValueOf(true))

	// make sure we can get it out with the compiler
	if !ut[unmarshalerText{"y", "z"}] {
		t.Errorf("Failed to look up up reflect-set map key with compiler")
	}

	utKeys := refut.MapKeys()

	// make sure keys extracted via reflection have the correct type
	if _, ok := utKeys[0].Interface().(unmarshalerText); !ok {
		t.Errorf("Map keys via MapKeys() have wrong type: %v", utKeys[0].Type().String())
	}

	// and via iteration

	utIter := refut.MapRange()
	utIter.Next()
	utIterKey := utIter.Key()

	if _, ok := utIterKey.Interface().(unmarshalerText); !ok {
		t.Errorf("Map keys via MapIter() have wrong type: %v", utIterKey.Type().String())
	}

	{
		m := map[any]any{1: 2}
		rv := ValueOf(m)
		iter := rv.MapRange()

		iter.Next()
		k := iter.Key()
		if k.Kind().String() != "interface" {
			t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
		}

		keys := rv.MapKeys()
		if k := keys[0]; k.Kind().String() != "interface" {
			t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
		}

	}
}

// For an interface type, it returns the number of exported and unexported methods.

type counter interface {
	count() int
}

type count struct {
	i int
}

func (c *count) count() int {
	return c.i
}

func TestMapInterfaceKeys(t *testing.T) {
	m := make(map[interface{}]int)
	for i := 0; i < 20; i++ {
		c := &count{i}
		m[c] = i
	}

	for k, v := range m {
		if got := m[k]; got != v {
			t.Error("lookup failure got", got, "want", v)
		}
	}

	refm := ValueOf(m)

	keys := refm.MapKeys()
	if len(keys) != 20 {
		t.Error("failed to look up 20 keys")
	}
	for _, k := range keys {
		c := k.Interface().(*count)
		e := refm.MapIndex(k)
		if e.Interface().(int) != c.i {
			t.Error("reflect lookup failure:", c.i)
		}
	}

	it := refm.MapRange()
	var totalKeys int
	for it.Next() {
		totalKeys++
		k := it.Key()
		v := it.Value().Interface().(int)

		c := k.Interface().(*count)
		if v != c.i || refm.MapIndex(k).Interface().(int) != c.i {
			t.Error("reflect iter lookup failure:", c.i)
		}
	}

	if totalKeys != 20 {
		t.Errorf("failed to get 20 keys")
	}
}

func TestMapInterfaceElem(t *testing.T) {
	m := make(map[string]interface{})
	refm := ValueOf(m)

	four := ValueOf(4)
	hello := ValueOf("hello")

	refm.SetMapIndex(hello, four)

	if v := refm.MapIndex(hello).Interface().(int); v != 4 {
		t.Errorf("failed to get value assigned to interface via MapIndex")
	}

	if v := m["hello"].(int); v != 4 {
		t.Errorf("failed to get value assigned to interface via direct lookup")
	}
}

func TestTinySlice(t *testing.T) {
	s := []int{0, 10, 20}
	refs := ValueOf(s)

	for i := 3; i < 10; i++ {
		refs = Append(refs, ValueOf(i*10))
	}

	s = refs.Interface().([]int)

	for i := 0; i < 10; i++ {
		if s[i] != i*10 {
			t.Errorf("s[%d]=%d, want %d", i, s[i], i*10)
		}
	}

	s28 := s[2:8]
	s28ref := refs.Slice(2, 8)

	if len(s28) != s28ref.Len() || cap(s28) != s28ref.Cap() {
		t.Errorf("Slice: len(s28)=%d s28ref.Len()=%d cap(s28)=%d s28ref.Cap()=%d\n", len(s28), s28ref.Len(), cap(s28), s28ref.Cap())
	}

	for i, got := range s28 {
		want := int(s28ref.Index(i).Int())
		if got != want {
			t.Errorf("s28[%d]=%d, want %d", i, got, want)
		}
	}

	s268 := s[2:6:8]
	s268ref := refs.Slice3(2, 6, 8)

	if len(s268) != s268ref.Len() || cap(s268) != s268ref.Cap() {
		t.Errorf("Slice3: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s268), s268ref.Len(), cap(s268), s268ref.Cap())
	}

	for i, got := range s268 {
		want := int(s268ref.Index(i).Int())
		if got != want {
			t.Errorf("s268[%d]=%d, want %d", i, got, want)
		}
	}

	// should be equivalent to s28 now, except for the capacity which doesn't change

	s268ref = ValueOf(&s268).Elem()
	s268ref.SetLen(6)
	if len(s28) != s268ref.Len() || cap(s268) != s268ref.Cap() {
		t.Errorf("SetLen: len(s268)=%d s268ref.Len()=%d cap(s268)=%d s268ref.Cap()=%d\n", len(s28), s268ref.Len(), cap(s268), s268ref.Cap())
	}

	for i, got := range s28 {
		want := int(s268ref.Index(i).Int())
		if got != want {
			t.Errorf("s28[%d]=%d, want %d", i, got, want)
		}
	}

	refs = MakeSlice(TypeOf(s), 5, 10)
	s = refs.Interface().([]int)

	if len(s) != refs.Len() || cap(s) != refs.Cap() {
		t.Errorf("len(s)=%v refs.Len()=%v cap(s)=%v refs.Cap()=%v", len(s), refs.Len(), cap(s), refs.Cap())
	}
}

func TestTinyBytes(t *testing.T) {
	s := []byte("abcde")
	refs := ValueOf(s)

	s2 := refs.Bytes()

	if !equal(s, s2) {
		t.Errorf("Failed to get Bytes(): %v != %v", s, s2)
	}

	Copy(refs, ValueOf("12345"))

	if string(s) != "12345" {
		t.Errorf("Copy()=%q, want `12345`", string(s))
	}

	// test small arrays that fit in a pointer
	a := [3]byte{10, 20, 30}
	v := ValueOf(&a)
	vslice := v.Elem().Bytes()
	if len(vslice) != 3 || cap(vslice) != 3 {
		t.Errorf("len(vslice)=%v, cap(vslice)=%v", len(vslice), cap(vslice))
	}

	for i, got := range vslice {
		if want := (byte(i) + 1) * 10; got != want {
			t.Errorf("vslice[%d]=%d, want %d", i, got, want)
		}
	}
}

func TestTinyNamedTypes(t *testing.T) {
	type namedString string

	named := namedString("foo")
	if got, want := TypeOf(named).Name(), "namedString"; got != want {
		t.Errorf("TypeOf.Name()=%v, want %v", got, want)
	}

	if got, want := TypeOf(named).String(), "reflect_test.namedString"; got != want {
		t.Errorf("TypeOf.String()=%v, want %v", got, want)
	}

	errorType := TypeOf((*error)(nil)).Elem()
	if s := errorType.String(); s != "error" {
		t.Errorf("error type = %v, want error", s)
	}

	m := make(map[[4]uint16]string)

	if got, want := TypeOf(m).String(), "map[[4]uint16]string"; got != want {
		t.Errorf("Type.String()=%v, want %v", got, want)
	}

	s := struct {
		a int8
		b int8
		c int8
		d int8
		e int8
		f int32
	}{}

	if got, want := TypeOf(s).String(), "struct { a int8; b int8; c int8; d int8; e int8; f int32 }"; got != want {
		t.Errorf("Type.String()=%v, want %v", got, want)
	}

	if got, want := ValueOf(m).String(), "<map[[4]uint16]string Value>"; got != want {
		t.Errorf("Value.String()=%v, want %v", got, want)
	}

	if got, want := TypeOf(base64.Encoding{}).String(), "base64.Encoding"; got != want {
		t.Errorf("Type.String(base64.Encoding{})=%v, want %v", got, want)
	}

	type Repository struct {
		RoleName *string `json:"role_name,omitempty"`
	}

	var repo *Repository
	v := ValueOf(&repo).Elem()
	n := New(v.Type().Elem())
	v.Set(n)
}

func TestTinyStruct(t *testing.T) {
	type barStruct struct {
		QuxString string
		BazInt    int
	}

	type foobar struct {
		Foo string `foo:"struct tag"`
		Bar barStruct
	}

	var fb foobar
	fb.Bar.QuxString = "qux"

	reffb := TypeOf(fb)

	q := reffb.FieldByIndex([]int{1, 0})
	if want := "QuxString"; q.Name != want {
		t.Errorf("FieldByIndex=%v, want %v", q.Name, want)
	}

	var ok bool
	q, ok = reffb.FieldByName("Foo")
	if q.Name != "Foo" || !ok {
		t.Errorf("FieldByName(Foo)=%v,%v, want Foo, true", q.Name, ok)
	}

	if got, want := q.Tag, `foo:"struct tag"`; string(got) != want {
		t.Errorf("StrucTag for Foo=%v, want %v", got, want)
	}

	q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "bar" })
	if q.Name != "Bar" || !ok {
		t.Errorf("FieldByNameFunc(bar)=%v,%v, want Bar, true", q.Name, ok)
	}

	q, ok = reffb.FieldByName("Snorble")
	if q.Name != "" || ok {
		t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false", q.Name, ok)
	}

	q, ok = reffb.FieldByNameFunc(func(s string) bool { return strings.ToLower(s) == "snorble" })
	if q.Name != "" || ok {
		t.Errorf("FieldByName(snorble)=%v,%v, want ``, false", q.Name, ok)
	}
}

func TestTinyZero(t *testing.T) {
	s := "hello, world"
	sptr := &s
	v := ValueOf(&sptr).Elem()
	v.Set(Zero(v.Type()))

	sptr = v.Interface().(*string)

	if sptr != nil {
		t.Errorf("failed to set a nil string pointer")
	}

	sl := []int{1, 2, 3}
	v = ValueOf(&sl).Elem()
	v.Set(Zero(v.Type()))
	sl = v.Interface().([]int)

	if sl != nil {
		t.Errorf("failed to set a nil slice")
	}
}

func addrDecode(body interface{}) {
	vbody := ValueOf(body)
	ptr := vbody.Elem()
	pptr := ptr.Addr()
	addrSetInt(pptr.Interface())
}

func addrSetInt(intf interface{}) {
	ptr := intf.(*uint64)
	*ptr = 112358
}

func TestTinyAddr(t *testing.T) {
	var n uint64
	addrDecode(&n)
	if n != 112358 {
		t.Errorf("Failed to set t=112358, got %v", n)
	}

	v := ValueOf(&n)
	if got, want := v.Elem().Addr().CanAddr(), false; got != want {
		t.Errorf("Elem.Addr.CanAddr=%v, want %v", got, want)
	}
}

func TestTinyNilType(t *testing.T) {
	var a any
	typ := TypeOf(a)
	if typ != nil {
		t.Errorf("Type of any{nil} is not nil")
	}
}

func TestTinySetBytes(t *testing.T) {
	var b []byte
	refb := ValueOf(&b).Elem()
	s := []byte("hello")
	refb.SetBytes(s)
	s[0] = 'b'

	refbSlice := refb.Interface().([]byte)

	if len(refbSlice) != len(s) || b[0] != s[0] || refbSlice[0] != s[0] {
		t.Errorf("SetBytes(): reflection slice mismatch")
	}
}

type methodStruct struct {
	i int
}

func (m methodStruct) ValueMethod1() int {
	return m.i
}

func (m methodStruct) valueMethod2() int {
	return m.i
}

func (m *methodStruct) PointerMethod1() int {
	return m.i
}

func (m *methodStruct) PointerMethod2() int {
	return m.i
}

func (m *methodStruct) pointerMethod3() int {
	return m.i
}

func TestTinyNumMethods(t *testing.T) {
	refptrt := TypeOf(&methodStruct{})
	if got, want := refptrt.NumMethod(), 1+2; got != want {
		t.Errorf("Pointer Methods=%v, want %v", got, want)
	}

	reft := refptrt.Elem()
	if got, want := reft.NumMethod(), 1; got != want {
		t.Errorf("Value Methods=%v, want %v", got, want)
	}
}

func TestAssignableTo(t *testing.T) {
	var a any
	refa := ValueOf(&a).Elem()
	refa.Set(ValueOf(4))
	if got, want := refa.Interface().(int), 4; got != want {
		t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
	}

	b := []byte{0x01, 0x02}
	refb := ValueOf(&b).Elem()
	refb.Set(ValueOf([]byte{0x02, 0x03}))
	if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
		t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
	}

	type bstr []byte

	c := bstr{0x01, 0x02}
	refc := ValueOf(&c).Elem()
	refc.Set(ValueOf([]byte{0x02, 0x03}))
	if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
		t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
	}

	d := []byte{0x01, 0x02}
	refd := ValueOf(&d).Elem()
	refd.Set(ValueOf(bstr{0x02, 0x03}))
	if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
		t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
	}
}

func TestConvertBasic(t *testing.T) {
	v := ValueOf(int64(3))
	c := v.Convert(TypeOf(byte(0)))

	if c.Type().Kind() != Uint8 || c.Uint() != 3 {
		t.Errorf("Convert(uint64 -> byte failed: kind=%v, value=%d", c.Type().Kind().String(), c.Uint())
	}

	v = ValueOf("hello")
	c = v.Convert(TypeOf([]byte("")))

	if c.Type().Kind() != Slice || c.Type().Elem().Kind() != Uint8 && c.Len() != 5 && string(c.Bytes()) != "hello" {
		t.Errorf("Convert(string -> []byte")
	}

	type namedString string

	c = v.Convert(TypeOf(namedString("")))
	if c.Type().Kind() != String || c.Type().Name() != "namedString" {
		t.Errorf("Convert(string -> namedString")
	}
}

func TestConvertSliceToArrayOrArrayPointer(t *testing.T) {
	s := make([]byte, 2, 4)
	// a0 := [0]byte(s)
	// a1 := [1]byte(s[1:])     // a1[0] == s[1]
	// a2 := [2]byte(s)         // a2[0] == s[0]
	// a4 := [4]byte(s)         // panics: len([4]byte) > len(s)

	v := ValueOf(s).Convert(TypeFor[[0]byte]())
	if v.Kind() != Array || v.Type().Len() != 0 {
		t.Error("Convert([]byte -> [0]byte)")
	}
	v = ValueOf(s[1:]).Convert(TypeFor[[1]byte]())
	if v.Kind() != Array || v.Type().Len() != 1 {
		t.Error("Convert([]byte -> [1]byte)")
	}
	v = ValueOf(s).Convert(TypeFor[[2]byte]())
	if v.Kind() != Array || v.Type().Len() != 2 {
		t.Error("Convert([]byte -> [2]byte)")
	}
	if ValueOf(s).CanConvert(TypeFor[[4]byte]()) {
		t.Error("Converting a slice with len smaller than array to array should fail")
	}

	// s0 := (*[0]byte)(s)      // s0 != nil
	// s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
	// s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
	// s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)
	v = ValueOf(s).Convert(TypeFor[*[0]byte]())
	if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 0 {
		t.Error("Convert([]byte -> *[0]byte)")
	}
	v = ValueOf(s[1:]).Convert(TypeFor[*[1]byte]())
	if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 1 {
		t.Error("Convert([]byte -> *[1]byte)")
	}
	v = ValueOf(s).Convert(TypeFor[*[2]byte]())
	if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 2 {
		t.Error("Convert([]byte -> *[2]byte)")
	}
	if ValueOf(s).CanConvert(TypeFor[*[4]byte]()) {
		t.Error("Converting a slice with len smaller than array to array pointer should fail")
	}

	// Test converting slices with backing arrays <= and >64bits
	slice64 := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
	array64 := ValueOf(slice64).Convert(TypeFor[[8]byte]()).Interface().([8]byte)
	if !bytes.Equal(slice64, array64[:]) {
		t.Errorf("converted array %x does not match backing array of slice %x", array64, slice64)
	}

	slice72 := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}
	array72 := ValueOf(slice72).Convert(TypeFor[[9]byte]()).Interface().([9]byte)
	if !bytes.Equal(slice72, array72[:]) {
		t.Errorf("converted array %x does not match backing array of slice %x", array72, slice72)
	}
}

func TestConvertToEmptyInterface(t *testing.T) {
	anyType := TypeFor[interface{}]()

	v := ValueOf(false).Convert(anyType)
	if v.Kind() != Interface || v.NumMethod() > 0 {
		t.Error("Convert(bool -> interface{})")
	}
	_ = v.Interface().(interface{}).(bool)

	v = ValueOf(int64(3)).Convert(anyType)
	if v.Kind() != Interface || v.NumMethod() > 0 {
		t.Error("Convert(int64 -> interface{})")
	}
	_ = v.Interface().(interface{}).(int64)

	v = ValueOf(struct{}{}).Convert(anyType)
	if v.Kind() != Interface || v.NumMethod() > 0 {
		t.Error("Convert(struct -> interface{})")
	}
	_ = v.Interface().(interface{}).(struct{})

	v = ValueOf([]struct{}{}).Convert(anyType)
	if v.Kind() != Interface || v.NumMethod() > 0 {
		t.Error("Convert(slice -> interface{})")
	}
	_ = v.Interface().(interface{}).([]struct{})

	v = ValueOf(map[string]string{"A": "B"}).Convert(anyType)
	if v.Kind() != Interface || v.NumMethod() > 0 {
		t.Error("Convert(map -> interface{})")
	}
	_ = v.Interface().(interface{}).(map[string]string)
}

func TestClearSlice(t *testing.T) {
	type stringSlice []string
	for _, test := range []struct {
		slice  any
		expect any
	}{
		{
			slice:  []bool{true, false, true},
			expect: []bool{false, false, false},
		},
		{
			slice:  []byte{0x00, 0x01, 0x02, 0x03},
			expect: []byte{0x00, 0x00, 0x00, 0x00},
		},
		{
			slice:  [][]int{[]int{2, 1}, []int{3}, []int{}},
			expect: [][]int{nil, nil, nil},
		},
		{
			slice:  []stringSlice{stringSlice{"hello", "world"}, stringSlice{}, stringSlice{"goodbye"}},
			expect: []stringSlice{nil, nil, nil},
		},
	} {
		v := ValueOf(test.slice)
		expectLen, expectCap := v.Len(), v.Cap()

		v.Clear()
		if len := v.Len(); len != expectLen {
			t.Errorf("Clear(slice) altered len, got %d, expected %d", len, expectLen)
		}
		if cap := v.Cap(); cap != expectCap {
			t.Errorf("Clear(slice) altered cap, got %d, expected %d", cap, expectCap)
		}
		if !DeepEqual(test.slice, test.expect) {
			t.Errorf("Clear(slice) got %v, expected %v", test.slice, test.expect)
		}
	}
}

func TestClearMap(t *testing.T) {
	for _, test := range []struct {
		m      any
		expect any
	}{
		{
			m:      map[int]bool{1: true, 2: false, 3: true},
			expect: map[int]bool{},
		},
		{
			m:      map[string][]byte{"hello": []byte("world")},
			expect: map[string][]byte{},
		},
	} {
		v := ValueOf(test.m)

		v.Clear()
		if len := v.Len(); len != 0 {
			t.Errorf("Clear(map) should set len to 0, got %d", len)
		}
		if !DeepEqual(test.m, test.expect) {
			t.Errorf("Clear(map) got %v, expected %v", test.m, test.expect)
		}
	}
}

func TestCopyArrayToSlice(t *testing.T) {
	// Test copying array <=64 bits and >64bits
	// See issue #4554
	a1 := [1]int64{1}
	s1 := make([]int64, 1)
	a2 := [2]int64{1, 2}
	s2 := make([]int64, 2)
	a8 := [8]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
	s8 := make([]byte, 8)
	a9 := [9]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}
	s9 := make([]byte, 9)

	Copy(ValueOf(s1), ValueOf(a1))
	if !slices.Equal(s1, a1[:]) {
		t.Errorf("copied slice %x does not match input array %x", s1, a1[:])
	}
	Copy(ValueOf(s2), ValueOf(a2))
	if !slices.Equal(s2, a2[:]) {
		t.Errorf("copied slice %x does not match input array %x", s2, a2[:])
	}
	Copy(ValueOf(s8), ValueOf(a8))
	if !bytes.Equal(s8, a8[:]) {
		t.Errorf("copied slice %x does not match input array %x", s8, a8[:])
	}
	Copy(ValueOf(s9), ValueOf(a9))
	if !bytes.Equal(s9, a9[:]) {
		t.Errorf("copied slice %x does not match input array %x", s9, a9[:])
	}
}

func TestIssue4040(t *testing.T) {
	var value interface{} = uint16(0)

	// get the pointer to the interface value
	inPtr := ValueOf(&value)

	// dereference to get the actual value (an interface)
	inElem := inPtr.Elem()

	// create a new value of the same concrete type
	uint16Type := TypeOf(uint16(0))
	newUint16Value := New(uint16Type).Elem()
	newUint16Value.Set(ValueOf(uint16(13)))

	// set the new value to the interface
	inElem.Set(newUint16Value)

	if value.(uint16) != 13 {
		t.Errorf("Failed to set interface value from uint16")
	}
}

func equal[T comparable](a, b []T) bool {
	if len(a) != len(b) {
		return false
	}

	for i, aa := range a {
		if b[i] != aa {
			return false
		}
	}
	return true
}

func TestTypeAssert(t *testing.T) {
	testTypeAssert(t, int(123456789), int(123456789), true)
	testTypeAssert(t, int(-123456789), int(-123456789), true)
	testTypeAssert(t, int32(123456789), int32(123456789), true)
	testTypeAssert(t, int8(-123), int8(-123), true)
	testTypeAssert(t, [2]int{1234, -5678}, [2]int{1234, -5678}, true)
	testTypeAssert(t, "test value", "test value", true)
	testTypeAssert(t, any("test value"), any("test value"), true)

	v := 123456789
	testTypeAssert(t, &v, &v, true)

	testTypeAssert(t, int(123), uint(0), false)

	testTypeAssert[any](t, 1, 1, true)
	testTypeAssert[fmt.Stringer](t, 1, nil, false)

	vv := testTypeWithMethod{"test"}
	testTypeAssert[any](t, vv, vv, true)
	testTypeAssert[any](t, &vv, &vv, true)
	testTypeAssert[fmt.Stringer](t, vv, vv, true)
	testTypeAssert[fmt.Stringer](t, &vv, &vv, true)
	testTypeAssert[interface{ A() }](t, vv, nil, false)
	testTypeAssert[interface{ A() }](t, &vv, nil, false)
	testTypeAssert(t, any(vv), any(vv), true)
	testTypeAssert(t, fmt.Stringer(vv), fmt.Stringer(vv), true)

	testTypeAssert(t, fmt.Stringer(vv), any(vv), true)
	testTypeAssert(t, any(vv), fmt.Stringer(vv), true)
	testTypeAssert(t, fmt.Stringer(vv), interface{ M() }(vv), true)
	testTypeAssert(t, interface{ M() }(vv), fmt.Stringer(vv), true)

	testTypeAssert(t, any(int(1)), int(1), true)
	testTypeAssert(t, any(int(1)), byte(0), false)
	testTypeAssert(t, fmt.Stringer(vv), vv, true)
}

func testTypeAssert[T comparable, V any](t *testing.T, val V, wantVal T, wantOk bool) {
	t.Helper()

	v, ok := TypeAssert[T](ValueOf(&val).Elem())
	if v != wantVal || ok != wantOk {
		t.Errorf("TypeAssert[%v](%#v) = (%#v, %v); want = (%#v, %v)", TypeFor[T](), val, v, ok, wantVal, wantOk)
	}

	// Additionally make sure that TypeAssert[T](v) behaves in the same way as v.Interface().(T).
	v2, ok2 := ValueOf(&val).Elem().Interface().(T)
	if v != v2 || ok != ok2 {
		t.Errorf("reflect.ValueOf(%#v).Interface().(%v) = (%#v, %v); want = (%#v, %v)", val, TypeFor[T](), v2, ok2, v, ok)
	}
}

func TestTypeAssertStruct(t *testing.T) {
	type taStruct struct {
		i int
		b bool
	}

	var a any

	// struct
	a = taStruct{3, true}
	if s, ok := a.(taStruct); ok {
		if s.i != 3 || s.b != true {
			t.Errorf("a.(S) failed: got s.i=%v, s.b=%v\n", s.i, s.b)
		}
	} else {
		t.Errorf("a.(S) failed: got ok=false")
	}

	if s, ok := TypeAssert[taStruct](ValueOf(a)); ok {
		if s.i != 3 || s.b != true {
			t.Errorf("TypeAssert[S] failed: got s.i=%v, s.b=%v\n", s.i, s.b)
		}
	} else {
		t.Errorf("TypeAssert[S] failed: got ok=false")
	}

	// struct ptr
	a = &taStruct{3, true}
	if s, ok := a.(*taStruct); ok {
		if s.i != 3 || s.b != true {
			t.Errorf("a.(*S) failed: got s.i=%v, s.b=%v\n", s.i, s.b)
		}
	} else {
		t.Errorf("a.(*S) failed: got ok=false")
	}

	if s, ok := TypeAssert[*taStruct](ValueOf(a)); ok {
		if s.i != 3 || s.b != true {
			t.Errorf("TypeAssert[*S] failed: got s.i=%v, s.b=%v\n", s.i, s.b)
		}
	} else {
		t.Errorf("TypeAssert[*S] failed: got ok=false")
	}

}

type testTypeWithMethod struct{ val string }

func (v testTypeWithMethod) String() string { return v.val }
func (v testTypeWithMethod) M()             {}

func TestTypeAssertPanic(t *testing.T) {
	if runtime.GOARCH == "wasm" {
		t.Log("recover not supported")
		return
	}

	t.Run("zero val", func(t *testing.T) {
		defer func() { recover() }()
		TypeAssert[int](Value{})
		t.Fatalf("TypeAssert did not panic")
	})
	t.Run("read only", func(t *testing.T) {
		defer func() { recover() }()
		TypeAssert[int](ValueOf(&testTypeWithMethod{}).FieldByName("val"))
		t.Fatalf("TypeAssert did not panic")
	})
}

// Functions needed by all_test.go

func IsRO(v Value) bool {
	return reflectlite.IsRO(v.Value)
}

func MakeRO(v Value) Value {
	return Value{reflectlite.MakeRO(v.Value, true)}
}
