package serialization

import (
	"reflect"

	"github.com/go-playground/validator/v10"
	gperr "github.com/yusing/goutils/errs"
)

var validate = validator.New()

var ErrValidationError = gperr.New("validation error")

func Validator() *validator.Validate {
	return validate
}

func MustRegisterValidation(tag string, fn validator.Func) {
	err := validate.RegisterValidation(tag, fn)
	if err != nil {
		panic(err)
	}
}

type CustomValidator interface {
	Validate() gperr.Error
}

var validatorType = reflect.TypeFor[CustomValidator]()

func ValidateWithCustomValidator(v reflect.Value) gperr.Error {
	if v.Kind() == reflect.Pointer {
		if v.IsNil() {
			// return nil
			return validateWithValidator(reflect.New(v.Type().Elem()))
		}
		if v.Type().Implements(validatorType) {
			return v.Interface().(CustomValidator).Validate()
		}
		return validateWithValidator(v.Elem())
	} else {
		vt := v.Type()
		if vt.PkgPath() != "" { // not a builtin type
			// prioritize pointer method
			if v.CanAddr() {
				vAddr := v.Addr()
				if vAddr.Type().Implements(validatorType) {
					return vAddr.Interface().(CustomValidator).Validate()
				}
			}
			// fallback to value method
			if vt.Implements(validatorType) {
				return v.Interface().(CustomValidator).Validate()
			}
		}
	}
	return nil
}

func validateWithValidator(v reflect.Value) gperr.Error {
	if v.Type().Implements(validatorType) {
		return v.Interface().(CustomValidator).Validate()
	}
	return nil
}
