package bizerr

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var (
	UserAlreadyRegistered = NewBusinessErrCode(1, 8888, "UserAlreadyRegistered")
)

// CHANGE THESE FOLLOWING CODES IS DANGEROUS,
// PLEASE MAKE SURE YOU KNOW WHAT YOU ARE DOING
func TestEnsureConstantValueNotChange(t *testing.T) {
	Convey("TestEnsureConstantValueNotChange", t, func() {
		So(requestErrCodeStart, ShouldEqual, 40000000)
		So(requestErrCodeCustomStart, ShouldEqual, 40000000)
		So(requestErrCodeCustomEnd, ShouldEqual, 49900000)
		So(requestErrCodeEnd, ShouldEqual, 49999999)

		So(internalErrCodeStart, ShouldEqual, 50000000)
		So(internalErrCodeCustomStart, ShouldEqual, 50000000)
		So(internalErrCodeCustomEnd, ShouldEqual, 59900000)
		So(internalErrCodeEnd, ShouldEqual, 59999999)

		So(businessErrCodeStart, ShouldEqual, 60000000)
		So(businessErrCodeCustomStart, ShouldEqual, 60000000)
		So(businessErrCodeCustomEnd, ShouldEqual, 69900000)
		So(businessErrCodeEnd, ShouldEqual, 69999999)
	})
}

func TestNewRequestErrCode(t *testing.T) {
	Convey("TestNewRequestErrCode", t, func() {
		Convey("normal case", func() {
			code := newRequestErrCode(1, 1, "1")
			So(code, ShouldEqual, 40100001)
		})
		Convey("should panic if invalid offset is given", func() {
			So(func() { newRequestErrCode(1, maxCustomErrCodeOffset+1, "t") }, ShouldPanicWith, "invalid offset")
		})
	})
}

func TestNewInternalErrCode(t *testing.T) {
	Convey("TestNewInternalErrCode", t, func() {
		Convey("normal case", func() {
			code := NewInternalErrCode(1, 1, "t")
			So(code, ShouldEqual, 50100001)
		})
		Convey("should panic if invalid offset is given", func() {
			So(func() { NewInternalErrCode(1, maxCustomErrCodeOffset+1, "t") }, ShouldPanicWith, "invalid offset")
		})
	})
}

func TestNewBusinessErrCode(t *testing.T) {
	Convey("TestNewBusinessErrCode", t, func() {
		Convey("normal case", func() {
			code := NewBusinessErrCode(1, 1, "t")
			So(code, ShouldEqual, 60100001)
		})
		Convey("should panic if invalid offset is given", func() {
			So(func() { NewBusinessErrCode(1, maxCustomErrCodeOffset+1, "t") }, ShouldPanicWith, "invalid offset")
		})
	})
}

func TestPrintAllCustomErrCode(t *testing.T) {
	Convey("TestPrintAllCustomErrCode", t, func() {
		PrintAllCustomErrCode()
	})
}

func Test_checkSvcIdAndOffset(t *testing.T) {
	Convey("Test_checkSvcIdAndOffset", t, func() {
		Convey("normal case", func() {
			So(func() { checkSvcIdAndOffset(1, 1) }, ShouldNotPanic)
		})
		Convey("invalid svcId", func() {
			So(func() { checkSvcIdAndOffset(0, 1) }, ShouldPanic)
			So(func() { checkSvcIdAndOffset(99, 1) }, ShouldPanic)
		})
		Convey("invalid offset", func() {
			So(func() { checkSvcIdAndOffset(1, 0) }, ShouldPanic)
			So(func() { checkSvcIdAndOffset(1, 9900000) }, ShouldPanic)
		})
	})
}

func Test_buildCustomErrCode(t *testing.T) {
	Convey("Test_buildCustomErrCode", t, func() {
		code := buildCustomErrCode(requestErrCodeCustomStart, 98, 99999)
		So(code, ShouldEqual, 49899999)

		code = buildCustomErrCode(requestErrCodeCustomStart, 1, 1)
		So(code, ShouldEqual, 40100001)
	})
}

func TestMustRegisterHttpStatus(t *testing.T) {
	Convey("TestMustRegisterHttpStatus", t, func() {
		Convey("normal case", func() {
			MustRegisterHttpStatus(ErrCodeRedis, 404)
		})
		Convey("should panic if the code has already been registered", func() {
			errCode2HttpStatus[ErrCodeRedis] = 404
			defer func() { // clear
				delete(errCode2HttpStatus, ErrCodeRedis)
			}()
			So(func() { MustRegisterHttpStatus(ErrCodeRedis, 404) }, ShouldPanicWith, "duplicate register http status")
		})
	})
}

func TestOverwriteHttpStatus(t *testing.T) {
	Convey("TestOverwriteHttpStatus", t, func() {
		defer func() { // clear
			delete(errCode2HttpStatus, ErrCodeRedis)
		}()
		OverwriteHttpStatus(ErrCodeRedis, 404)
		OverwriteHttpStatus(ErrCodeRedis, 401) // overwrite
		So(errCode2HttpStatus[ErrCodeRedis], ShouldEqual, 401)
	})
}

func Test_checkAndSetCustomErrCode(t *testing.T) {
	Convey("Test_checkAndSetCustomErrCode", t, func() {
		Convey("normal case", func() {
			defer func() {
				delete(customErrCodeMap, ErrCode(952799))
			}()

			So(func() { checkAndSetCustomErrCode(ErrCode(952799), "t") }, ShouldNotPanic)
		})
		Convey("should panic if the code has already been registered", func() {
			customErrCodeMap[ErrCode(952799)] = "t"
			defer func() {
				delete(customErrCodeMap, ErrCode(952799))
			}()

			So(func() { checkAndSetCustomErrCode(ErrCode(952799), "t") }, ShouldPanic)
		})
	})
}

func Test_convert2Exception(t *testing.T) {
	Convey("Test_convert2Exception", t, func() {
		// already is exception
		So(convert2Exception(ErrCode(9527)+exceptionOffset), ShouldEqual, ErrCode(100009527))
		// error is given
		So(convert2Exception(ErrCode(9527)), ShouldEqual, ErrCode(100009527))
	})
}

func Test_convert2Error(t *testing.T) {
	Convey("Test_convert2Error", t, func() {
		// already is error
		So(convert2Error(ErrCode(9527)), ShouldEqual, ErrCode(9527))
		// exception is given
		So(convert2Error(ErrCode(9527)+exceptionOffset), ShouldEqual, ErrCode(9527))
	})
}

func Test_isException(t *testing.T) {
	Convey("Test_isException", t, func() {
		So(IsException(ErrCode(100009527)), ShouldBeTrue)
		So(IsException(ErrCode(9527)), ShouldBeFalse)
	})
}

func Test_isRequestErr(t *testing.T) {
	Convey("Test_isRequestErr ", t, func() {
		So(IsRequestErr(ErrCodeParams), ShouldBeTrue)
		So(IsRequestErr(convert2Exception(ErrCodeParams)), ShouldBeTrue)

		// negative test
		So(IsRequestErr(ErrCodeDB), ShouldBeFalse)
		So(IsRequestErr(UserAlreadyRegistered), ShouldBeFalse)
	})
}

func Test_isInternalErr(t *testing.T) {
	Convey("Test_isInternalErr ", t, func() {
		So(IsInternalErr(ErrCodeDB), ShouldBeTrue)
		So(IsInternalErr(convert2Exception(ErrCodeDB)), ShouldBeTrue)

		// negative test
		So(IsInternalErr(ErrCodeParams), ShouldBeFalse)
		So(IsInternalErr(UserAlreadyRegistered), ShouldBeFalse)
	})
}

func Test_isBusinessErr(t *testing.T) {
	Convey("Test_isBusinessErr ", t, func() {
		code := NewBusinessErrCode(1, 12345, "t")
		So(IsBusinessErr(code), ShouldBeTrue)
		So(IsBusinessErr(convert2Exception(code)), ShouldBeTrue)

		// negative test
		So(IsBusinessErr(ErrCodeParams), ShouldBeFalse)
		So(IsBusinessErr(ErrCodeDB), ShouldBeFalse)
	})
}
