package container

import (
	"github.com/go-xorm/xorm"
	"reflect"
	"testing"
)

func TestLogBroker_Call_WithNull(t *testing.T) {

	out := []reflect.Value{reflect.ValueOf(123), reflect.ValueOf("456"), reflect.ValueOf(nil)}
	parameters := []reflect.Value{reflect.ValueOf(nil), reflect.ValueOf(456)}

	synchronizer1 := NewSynchronizer()
	synchronizer2 := NewSynchronizer()

	userId := "userId"
	user := &models.User{UserId: userId}
	callNumberOfTimes := 0
	sessionContext, err := NewSessionContext(user, transactionHandlerFactoryMock)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}
	var inFromCalling []reflect.Value = nil
	caller := func(in []reflect.Value) []reflect.Value {
		callNumberOfTimes++
		synchronizer1.Awake()
		synchronizer2.Wait()
		inFromCalling = in
		return out
	}
	builder := new(LogBrokerBuilder)
	broker, err := builder.Build(sessionContext, caller)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}

	var result []reflect.Value = nil
	go func() {
		result = broker(parameters)
		synchronizer1.Awake()
	}()
	synchronizer1.Wait()
	if callNumberOfTimes != 1 {
		t.Fatalf("调用中次数错误!")
	}
	synchronizer2.Awake()
	synchronizer1.Wait()

	if callNumberOfTimes != 1 {
		t.Fatalf("结束时调用次数错误!")
	}

	// Begin 检查参数
	if len(parameters) != len(inFromCalling) {
		t.Fatalf("参数长度错误!")
	}
	for i := 1; i < len(parameters); i++ {
		if parameters[i].Interface() != inFromCalling[i].Interface() {
			t.Fatalf("参数内容错误!")
		}
	}
	// End 检查参数

	// Begin 检查返回值
	if len(result) != len(out) {
		t.Fatalf("返回值长度错误!")
	}
	lastReturnValue := result[0].Interface()
	if err, ok := lastReturnValue.(error); ok {
		t.Fatalf("意外错误:%s", err.Error())
	}
	for i := 0; i < len(result); i++ {
		if result[i].Kind() != reflect.Invalid {
			if out[i].Kind() != reflect.Invalid {
				if result[i].Interface() != out[i].Interface() {
					t.Fatalf("返回值内容错误!")
				}
			} else {
				t.Fatalf("返回值内容错误!")
			}
		} else {
			if out[i].Kind() != reflect.Invalid {
				t.Fatalf("返回值内容错误!")
			}
		}
	}
	// End 检查返回值
}

func TestLogBroker_Call_WithNullPointer(t *testing.T) {

	var nullSession *xorm.Session = nil
	out := []reflect.Value{reflect.ValueOf(123), reflect.ValueOf("456"), reflect.ValueOf(nil)}
	parameters := []reflect.Value{reflect.ValueOf(nullSession), reflect.ValueOf(456)}

	synchronizer1 := NewSynchronizer()
	synchronizer2 := NewSynchronizer()

	userId := "userId"
	user := &models.User{UserId: userId}
	callNumberOfTimes := 0
	sessionContext, err := NewSessionContext(user, transactionHandlerFactoryMock)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}
	var inFromCalling []reflect.Value = nil
	caller := func(in []reflect.Value) []reflect.Value {
		callNumberOfTimes++
		synchronizer1.Awake()
		synchronizer2.Wait()
		inFromCalling = in
		return out
	}
	builder := new(LogBrokerBuilder)
	broker, err := builder.Build(sessionContext, caller)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}

	var result []reflect.Value = nil
	go func() {
		result = broker(parameters)
		synchronizer1.Awake()
	}()
	synchronizer1.Wait()
	if callNumberOfTimes != 1 {
		t.Fatalf("调用中次数错误!")
	}
	synchronizer2.Awake()
	synchronizer1.Wait()

	if callNumberOfTimes != 1 {
		t.Fatalf("结束时调用次数错误!")
	}

	// Begin 检查参数
	if len(parameters) != len(inFromCalling) {
		t.Fatalf("参数长度错误!")
	}
	for i := 1; i < len(parameters); i++ {
		if parameters[i].Interface() != inFromCalling[i].Interface() {
			t.Fatalf("参数内容错误!")
		}
	}
	// End 检查参数

	// Begin 检查返回值
	if len(result) != len(out) {
		t.Fatalf("返回值长度错误!")
	}
	lastReturnValue := result[0].Interface()
	if err, ok := lastReturnValue.(error); ok {
		t.Fatalf("意外错误:%s", err.Error())
	}
	for i := 0; i < len(result); i++ {
		if result[i].Kind() != reflect.Invalid {
			if out[i].Kind() != reflect.Invalid {
				if result[i].Interface() != out[i].Interface() {
					t.Fatalf("返回值内容错误!")
				}
			} else {
				t.Fatalf("返回值内容错误!")
			}
		} else {
			if out[i].Kind() != reflect.Invalid {
				t.Fatalf("返回值内容错误!")
			}
		}
	}
	// End 检查返回值
}

func TestLogBroker_Call_Panic(t *testing.T) {

	userId := "userId"
	user := &models.User{UserId: userId}
	sessionContext, err := NewSessionContext(user, transactionHandlerFactoryMock)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}
	var panicValue = "模拟崩溃!"
	caller := func(in []reflect.Value) []reflect.Value {
		panic(panicValue)
	}
	builder := new(LogBrokerBuilder)
	broker, err := builder.Build(sessionContext, caller)
	if err != nil {
		t.Fatalf("意外错误:%s", err.Error())
	}
	defer func() {
		if p := recover(); p != nil && p != panicValue {
			t.Fatalf("意外崩溃:%v", p)
		}
	}()
	result := broker([]reflect.Value{reflect.ValueOf(nil)})
	if len(result) > 0 {
		lastReturnValue := result[0].Interface()
		if lastReturnValue == nil {
			t.Fatalf("返回值为空!")
		}
		err, ok := lastReturnValue.(error)
		if ok {
			t.Fatalf("意外错误:%s", err.Error())
		}
	}
	t.Fatalf("意外未崩溃!")
}
