package assert

import (
	"fmt"
	"reflect"
	"strings"
)

var Asserter = NewAssert()

// Assert utility class
type Assert struct{}

// NewAssert returns a new instance of Assert
func NewAssert() *Assert {
	return &Assert{}
}

// NotEmpty checks if the given value is not empty
func (a *Assert) NotEmpty(value interface{}, message string) {
	if isEmptyValue(reflect.ValueOf(value)) {
		panic(fmt.Sprintf("Assertion failed: %s", message))
	}
}

// NotBlank checks if the given string is not blank
func (a *Assert) NotBlank(str string, message string) {
	if str == "" || len(strings.TrimSpace(str)) == 0 {
		panic(fmt.Sprintf("Assertion failed: %s", message))
	}
}

// Equals checks if two values are equal
func (a *Assert) Equals(expected, actual interface{}, message string) {
	if !reflect.DeepEqual(expected, actual) {
		panic(fmt.Sprintf("Assertion failed: %s. Expected '%v' but got '%v'", message, expected, actual))
	}
}

// Nil checks if the given value is nil
func (a *Assert) Nil(value interface{}, message string) {
	if value != nil {
		panic(fmt.Sprintf("Assertion failed: %s", message))
	}
}

// NotNil checks if the given value is not nil
func (a *Assert) NotNil(value interface{}, message string) {
	if value == nil {
		panic(fmt.Sprintf("Assertion failed: %s", message))
	}
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Map:
		return v.IsNil() || v.Len() == 0
	case reflect.Ptr:
		if v.IsNil() {
			return true
		}
		return isEmptyValue(v.Elem())
	default:
		return false
	}
}
