package reflectutils

import (
	"github.com/pkg/errors"
	"reflect"
	"strconv"
)

// GroupValueKind 将反射的Kind值进行聚集
func GroupValueKind(v reflect.Value) reflect.Kind {
	kind := v.Kind()

	switch {
	case kind >= reflect.Int && kind <= reflect.Int64:
		return reflect.Int64
	case kind >= reflect.Uint && kind <= reflect.Uint64:
		return reflect.Uint64
	case kind >= reflect.Float32 && kind <= reflect.Float64:
		return reflect.Float64
	default:
		return kind
	}
}

func PointerValueElem(v reflect.Value) reflect.Value {
	elem := v
	if v.Kind() == reflect.Ptr {
		elem = v.Elem()
	}

	return elem
}

func SliceValueElem(v reflect.Value) reflect.Value {
	elem := v
	if v.Kind() == reflect.Slice {
		elem = v.Elem()
	}

	return elem
}

func IsSliceValueOf(v reflect.Value, elementKind reflect.Kind) bool {
	return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == elementKind
}

func IsValueInteger(v reflect.Value) bool {
	return v.Kind() >= reflect.Int && v.Kind() <= reflect.Int64
}

func IsValueUnsignedInteger(v reflect.Value) bool {
	return v.Kind() >= reflect.Uint && v.Kind() <= reflect.Uint64
}

func IsValueFloat(v reflect.Value) bool {
	return v.Kind() >= reflect.Float32 && v.Kind() <= reflect.Float64
}

func IsValueStructPointer(v reflect.Value) bool {
	return v.Kind() == reflect.Pointer && v.Elem().Kind() == reflect.Struct
}

func IsValueStructOrStructPointer(v reflect.Value) bool {
	return v.Kind() == reflect.Struct || IsValueStructPointer(v)
}

func IsValueStructSliceOrStructSlicePointer(v reflect.Value) bool {
	return IsSliceValueOf(v, reflect.Struct) || (v.Kind() == reflect.Pointer && IsSliceValueOf(v.Elem(), reflect.Struct))
}

func IsValueTime(v reflect.Value) bool {
	return v.Kind() == reflect.Struct && v.Type().String() == "time.Time"
}

func IsValueTimePointer(v reflect.Value) bool {
	return v.Kind() == reflect.Pointer && IsValueTime(v.Elem())
}

// AssignStringValue 将any类型的值进行转化,赋值给string的reflect.Value
func AssignStringValue(data any, val reflect.Value) error {
	if data == nil {
		return nil
	}

	if val.Kind() != reflect.String {
		return errors.New("val应当为string类型的Value")
	}

	v, err := ToString(data)
	if err != nil {
		return err
	}

	val.SetString(v)

	return nil
}

// AssignInt64Value 将any类型的值进行转化,赋值给int64类型的reflect.Value
func AssignInt64Value(data any, val reflect.Value) error {
	if data == nil {
		return nil
	}

	if val.Kind() < reflect.Int && val.Kind() > reflect.Int64 {
		return errors.New("val应当为int类型的Value")
	}

	v, err := ToInt64(data)
	if err != nil {
		return err
	}

	val.SetInt(v)

	return nil
}

// AssignUint64Value 将any类型的值进行转化,赋值给uint64类型的reflect.Value
func AssignUint64Value(data any, val reflect.Value) error {
	if data == nil {
		return nil
	}

	if val.Kind() < reflect.Uint && val.Kind() > reflect.Uint64 {
		return errors.New("val应当为uint类型的Value")
	}

	v, err := ToUint64(data)
	if err != nil {
		return err
	}

	val.SetUint(v)

	return nil
}

// AssignBoolValue 将any类型的值进行转化,赋值给bool类型的reflect.Value
func AssignBoolValue(data any, val reflect.Value) error {
	if data == nil {
		return nil
	}

	if val.Kind() != reflect.Bool {
		return errors.New("val应当为bool类型的Value")
	}

	v, err := ToBool(data)
	if err != nil {
		return err
	}

	val.SetBool(v)

	return nil
}

// AssignFloat64Value 将any类型的值进行转化,赋值给float64类型的reflect.Value
func AssignFloat64Value(data any, val reflect.Value) error {
	if data == nil {
		return nil
	}

	if val.Kind() != reflect.Float32 && val.Kind() != reflect.Float64 {
		return errors.New("val应当为float类型的Value")
	}

	v, err := ToFloat64(data)
	if err != nil {
		return err
	}

	val.SetFloat(v)

	return nil
}

// ToString 将any类型的值进行转化为string类型
func ToString(data any) (string, error) {
	if data == nil {
		return "", nil
	}

	dataVal := reflect.Indirect(reflect.ValueOf(data))
	dataKind := GroupValueKind(dataVal)

	switch dataKind {
	case reflect.String:
		return dataVal.String(), nil
	case reflect.Bool:
		if dataVal.Bool() {
			return "1", nil
		} else {
			return "0", nil
		}
	case reflect.Int64:
		return strconv.FormatInt(dataVal.Int(), 10), nil
	case reflect.Uint64:
		return strconv.FormatUint(dataVal.Uint(), 10), nil
	case reflect.Float64:
		return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
	case reflect.Slice, reflect.Array:
		elemKind := dataVal.Type().Elem().Kind()

		if elemKind != reflect.Uint8 {
			return "", errors.New("不支持的类型: " + dataVal.Type().String())
		}

		var uints []uint8
		if dataKind == reflect.Array {
			uints = make([]uint8, dataVal.Len(), dataVal.Len())
			for i := range uints {
				uints[i] = dataVal.Index(i).Interface().(uint8)
			}
		} else {
			uints = dataVal.Interface().([]uint8)
		}

		return string(uints), nil
	default:
		return "", errors.New("不支持的类型: " + dataVal.Type().String())
	}
}

// ToInt64 将any类型的值进行转化为int64类型
func ToInt64(data any) (int64, error) {
	if data == nil {
		return 0, nil
	}

	dataVal := reflect.Indirect(reflect.ValueOf(data))
	dataKind := GroupValueKind(dataVal)

	switch dataKind {
	case reflect.Int64:
		return dataVal.Int(), nil
	case reflect.Uint64:
		return int64(dataVal.Uint()), nil
	case reflect.Float64:
		return int64(dataVal.Float()), nil
	case reflect.Bool:
		if dataVal.Bool() {
			return 1, nil
		} else {
			return 0, nil
		}
	case reflect.String:
		str := dataVal.String()
		if str == "" {
			str = "0"
		}

		return strconv.ParseInt(str, 0, 10)
	default:
		return 0, errors.New("不支持的类型: " + dataVal.Type().String())
	}
}

// ToUint64 将any类型的值进行转化为uint64类型
func ToUint64(data any) (uint64, error) {
	if data == nil {
		return 0, nil
	}

	dataVal := reflect.Indirect(reflect.ValueOf(data))
	dataKind := GroupValueKind(dataVal)

	switch dataKind {
	case reflect.Int64:
		return uint64(dataVal.Int()), nil
	case reflect.Uint64:
		return dataVal.Uint(), nil
	case reflect.Float64:
		return uint64(dataVal.Float()), nil
	case reflect.Bool:
		if dataVal.Bool() {
			return 1, nil
		} else {
			return 0, nil
		}
	case reflect.String:
		str := dataVal.String()
		if str == "" {
			str = "0"
		}

		return strconv.ParseUint(str, 0, 10)
	default:
		return 0, errors.New("不支持的类型: " + dataVal.Type().String())
	}
}

// ToBool 将any类型的值进行转化为bool类型
func ToBool(data any) (bool, error) {
	if data == nil {
		return false, nil
	}

	dataVal := reflect.Indirect(reflect.ValueOf(data))
	dataKind := GroupValueKind(dataVal)

	switch dataKind {
	case reflect.Bool:
		return dataVal.Bool(), nil
	case reflect.Int64:
		return dataVal.Int() != 0, nil
	case reflect.Uint64:
		return dataVal.Uint() != 0, nil
	case reflect.Float64:
		return dataVal.Float() != 0, nil
	case reflect.String:
		if dataVal.String() == "" {
			return false, nil
		} else {
			return strconv.ParseBool(dataVal.String())
		}
	default:
		return false, errors.New("不支持的类型: " + dataVal.Type().String())
	}
}

// ToFloat64 将any类型的值进行转化为float64类型
func ToFloat64(data any) (float64, error) {
	if data == nil {
		return 0, nil
	}

	dataVal := reflect.Indirect(reflect.ValueOf(data))
	dataKind := GroupValueKind(dataVal)

	switch dataKind {
	case reflect.Int64:
		return float64(dataVal.Int()), nil
	case reflect.Uint64:
		return float64(dataVal.Uint()), nil
	case reflect.Float64:
		return dataVal.Float(), nil
	case reflect.Bool:
		if dataVal.Bool() {
			return 1, nil
		} else {
			return 0, nil
		}
	case reflect.String:
		str := dataVal.String()
		if str == "" {
			str = "0"
		}

		return strconv.ParseFloat(str, 10)
	default:
		return 0, errors.New("不支持的类型: " + dataVal.Type().String())
	}
}

func Zero[T any]() T {
	var zeroT T

	zeroAnyValue := reflect.ValueOf(zeroT)
	zeroAny := ZeroValueToAny(zeroAnyValue)
	if zeroAny == nil {
		return zeroT
	}

	return zeroAny.(T)
}

func ZeroValueToAny(v reflect.Value) any {
	if v.Kind() == reflect.Invalid {
		return nil
	}

	zeroValue := reflect.New(v.Type()).Elem()
	if zeroValue.Kind() != reflect.Pointer {
		zero(&zeroValue)
		return zeroValue.Interface()
	}

	zeroValue.Set(reflect.New(zeroValue.Type().Elem()))
	elemValue := PointerValueElem(zeroValue)
	zero(&elemValue)

	return zeroValue.Interface()
}

func zero(v *reflect.Value) {
	if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
		v.Set(reflect.MakeSlice(v.Type(), 0, 0))
	} else if v.Kind() == reflect.Map {
		v.Set(reflect.MakeMap(v.Type()))
	} else if v.Kind() == reflect.Chan {
		v.Set(reflect.MakeChan(v.Type(), 0))
	} else {
		v.Set(reflect.New(v.Type()).Elem())
	}
}