package reflectutils

import (
	"fmt"
	"github.com/pkg/errors"
	"reflect"
	"testing"
	"time"
)

func TestStruct(t *testing.T) {
	name := "test"
	age := 18
	enterTime := time.Now().Local()

	studentStruct := NewStruct(
		StructFieldDefinition{
			Name: "Name",
			Type: reflect.TypeOf(""),
			Tag:  "json:name",
		},
		StructFieldDefinition{
			Name: "Age",
			Type: reflect.TypeOf(0),
			Tag:  "json:age",
		},
		StructFieldDefinition{
			Name: "EnterTime",
			Type: reflect.TypeOf(time.Time{}),
			Tag:  "json:enterTime",
		},
	)

	studentStruct.SetFieldValues(map[string]any{
		"Name":      name,
		"Age":       age,
		"EnterTime": enterTime,
	})

	values := studentStruct.FieldValues("Name", "Age", "EnterTime")

	for fieldName, value := range values {
		switch fieldName {
		case "Name":
			if value != name {
				t.Fatalf("%+v\n", errors.Errorf("名字不一致: except: %v, actual: %v", name, value))
			}
		case "Age":
			if value != age {
				t.Fatalf("%+v\n", errors.Errorf("年龄不一致: except: %v, actual: %v", age, value))
			}
		case "EnterTime":
			if value != enterTime {
				t.Fatalf("%+v\n", errors.Errorf("入学时间不一致: except: %v, actual: %v", enterTime, value))
			}
		default:
			t.Fatalf("%+v\n", errors.New("不存在的字段"))
		}
	}

	studentStruct.MakeMethod(
		StructMethodDefinition{
			Name:             "Print",
			ArgTypes:         []reflect.Type{reflect.TypeOf("")},
			ReturnValueTypes: []reflect.Type{reflect.TypeOf(errors.New(""))},
			Body: func(s *Struct, args ...any) []any {
				values := studentStruct.FieldValues("Name", "Age", "EnterTime")

				fmt.Println("Student Info:")
				fmt.Println("Name:", values["Name"])
				fmt.Println("Age:", values["Age"])
				fmt.Println("EnterTime:", values["EnterTime"].(time.Time).Format(time.DateTime))
				fmt.Println("Arg:", args[0])

				return []any{nil}
			},
		},
	)

	returns := studentStruct.CallMethod("Print", "Hello Args")
	if returns[0] != nil {
		t.Fatalf("%+v\n", errors.Errorf("%v", returns[0]))
	}
}

func TestFunction(t *testing.T) {
	printHelloFunc := MakeFunction(FunctionDefinition{
		ArgTypes:         []reflect.Type{reflect.TypeOf("")},
		ReturnValueTypes: []reflect.Type{reflect.TypeOf(errors.New(""))},
		Body: func(args ...any) []any {
			fmt.Printf("Hello %v!\n", args[0])
			return []any{nil}
		},
	})

	returns := printHelloFunc.Call("World")
	if returns[0] != nil {
		t.Fatalf("%+v\n", errors.Errorf("%v", returns[0]))
	}
}