package ii

import (
	"fmt"
	"reflect"

	"git.simanc.com/software/golib/v4/features/mo"
)

var (
	errTypeReturn = func(f *FieldInfo, v any) error {
		return fmt.Errorf("%s: %s's value type muse be %s, got: %s", getCallerName(), f.Name, f.Type.String(), valueType(v))
	}
	errRequired = func(name string, v any) error {
		return fmt.Errorf("%s: %s's value are Required, got value: %v", getCallerName(), name, v)
	}
	errValidate = func(err error, f *FieldInfo) error {
		return fmt.Errorf("%s: Validate failed: %s: %s(%s)", getCallerName(), err, f.Label, f.Name)
	}
	errMinReturn = func(f *FieldInfo, min float64) error {
		return fmt.Errorf("%s: %f < Minimum(%f)", getCallerName(), min, f.Minimum)
	}
	errMaxReturn = func(f *FieldInfo, max float64) error {
		return fmt.Errorf("%s: %f > Maximum(%f)", getCallerName(), max, f.Maximum)
	}
	errEnumReturn = func(f *FieldInfo, v any) error {
		return fmt.Errorf("%s: %v not in Enums group", getCallerName(), v)
	}
)

// Validate 用于校验传入的 value 是否符合该字段的数据类型.
// 注意: 即使 Required == false 当调用 Validate 时也会验证数据是否合法, 否则你应该在上层代码中移除该字段
func (f *FieldInfo) Validate(value any) error {
	if value == nil {
		return errRequired(f.Name, value)
	}
	switch f.Type {
	case mo.TypeDouble:
		return f.validateDouble(value)
	case mo.TypeString:
		return f.validateString(value)
	case mo.TypeObject:
		return f.validateObject(value)
	case mo.TypeArray:
		return f.validateArray(value)
	case mo.TypeBinary:
		return f.validateBinary(value)
	case mo.TypeObjectId:
		return f.validateObjectID(value)
	case mo.TypeBoolean:
		return f.validateBoolean(value)
	case mo.TypeDateTime:
		return f.validateDateTime(value)
	case mo.TypeInt32:
		return f.validateInt32(value)
	case mo.TypeInt64:
		return f.validateInt64(value)
	default:
		return fmt.Errorf("unsupported type: %s", valueType(f.Type))
	}
}

func (f *FieldInfo) validateDouble(value any) error {
	v, ok := value.(float64)
	if !ok {
		return errTypeReturn(f, value)
	}
	if f.Minimum != 0 && v < f.Minimum {
		return errMinReturn(f, v)
	}
	if f.Maximum != 0 && v > f.Maximum {
		return errMaxReturn(f, v)
	}
	if !f.inEnums(v) {
		return errEnumReturn(f, v)
	}
	return nil
}

func (f *FieldInfo) validateString(value any) error {
	v, ok := value.(string)
	if !ok {
		return errTypeReturn(f, value)
	}
	length := float64(len(v))
	if f.Minimum != 0 && length < f.Minimum {
		return errMinReturn(f, length)
	}
	if f.Maximum != 0 && length > f.Maximum {
		return errMaxReturn(f, length)
	}
	if !f.inEnums(v) {
		return errEnumReturn(f, v)
	}
	if f.pattern != nil {
		if !f.pattern.MatchString(v) {
			return fmt.Errorf("validateString: Pattern not matched(%s)", v)
		}
	}
	return nil
}

// validateObject
// 2023/01/28: from eric: object/map 类型的数据不允许 value 再次作为 map, 即只能存在一层 map
func (f *FieldInfo) validateObject(value any) error {
	if value == nil {
		return errNil
	}

	rv := reflect.ValueOf(value)
	if rv.Type().Kind() != reflect.Slice && rv.Type().Kind() != reflect.Array {
		return errTypeReturn(f, value)
	}

	if f.NoField {
		return nil
	}
	rvLen := rv.Len()

	length := float64(rvLen)
	if f.Minimum != 0 && length < f.Minimum {
		return errMinReturn(f, length)
	}

	if f.Maximum != 0 && length > f.Maximum {
		return errMaxReturn(f, length)
	}
	for i := 0; i < rvLen; i++ {
		ele, ok := rv.Index(i).Interface().(mo.E)
		if !ok {
			return errTypeReturn(f, value)
		}
		sub, found := f.SubField(ele.Key)
		if !found {
			return fmt.Errorf("validateObject: not found key: %s", sub.Name)
		}
		if err := sub.Validate(ele.Value); err != nil {
			return err
		}
	}
	return nil
}

// validateArray 校验数组
// 如果 Items == "array" 时则仅判断长度
// 如果 Items == "object" 除判断长度之外会进一步判断 map 中是否包含 Fields.Name
func (f *FieldInfo) validateArray(value any) error {
	if value == nil {
		return errNil
	}

	rv := reflect.ValueOf(value)
	if rv.Type().Kind() != reflect.Slice && rv.Type().Kind() != reflect.Array {
		return errTypeReturn(f, value)
	}

	length := float64(rv.Len())

	if f.Minimum != 0 && length < f.Minimum {
		return errMinReturn(f, length)
	}
	if f.Maximum != 0 && length > f.Maximum {
		return errMaxReturn(f, length)
	}

	switch f.Items {
	case FieldItemsArray, "":
		for i := 0; i < int(length); i++ {
			eleType := rv.Index(i).Kind()
			if eleType == reflect.Array || eleType == reflect.Slice {
				return fmt.Errorf("validateArray: the %d element type can not be %s", i, eleType.String())
			}
			if eleType == reflect.Map {
				if err := f.validateObject(rv.Index(i).Interface()); err != nil {
					return fmt.Errorf("validateArray: %s", err)
				}
			}
		}
	case FieldItemsObject:
		for i := 0; i < int(length); i++ {
			if err := f.validateObject(rv.Index(i).Interface()); err != nil {
				return fmt.Errorf("validateArray: %s", err)
			}
		}
	case FieldItemsObjectId:
		for i := 0; i < int(length); i++ {
			eleType := rv.Index(i)
			if oid, ok := eleType.Interface().(mo.ObjectID); ok && !oid.IsZero() {
				continue
			}
			return fmt.Errorf("validateArray: the %d element type can not be %s", i, eleType.Kind())
		}
	default:
		return fmt.Errorf("validateArray: unknown items: %s", f.Items)
	}
	return nil
}

func (f *FieldInfo) validateBinary(value any) error {
	var length float64
	switch v := value.(type) {
	case []byte:
		length = float64(len(v))
	case mo.Binary:
		length = float64(len(v.Data))
	default:
		return errTypeReturn(f, value)
	}
	if f.Minimum != 0 && length < f.Minimum {
		return errMinReturn(f, length)
	}
	if f.Maximum != 0 && length > f.Maximum {
		return errMaxReturn(f, length)
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) validateObjectID(value any) error {
	val, ok := value.(mo.ObjectID)
	if !ok {
		return errTypeReturn(f, value)
	}
	if f.Name == mo.OID {
		if val.IsZero() {
			return errTypeReturn(f, val)
		}
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) validateBoolean(value any) error {
	_, ok := value.(bool)
	if !ok {
		return errTypeReturn(f, value)
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) validateDateTime(value any) error {
	val, ok := value.(mo.DateTime)
	if !ok {
		return errTypeReturn(f, value)
	}
	if val.Time().IsZero() {
		return errTypeReturn(f, value)
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) validateInt32(value any) error {
	v, ok := value.(int32)
	if !ok {
		return errTypeReturn(f, value)
	}
	if f.Minimum != 0 && float64(v) < f.Minimum {
		return errMinReturn(f, float64(v))
	}
	if f.Maximum != 0 && float64(v) > f.Maximum {
		return errMaxReturn(f, float64(v))
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) validateInt64(value any) error {
	v, ok := value.(int64)
	if !ok {
		return errTypeReturn(f, value)
	}
	if f.Minimum != 0 && float64(v) < f.Minimum {
		return errMinReturn(f, float64(v))
	}
	if f.Maximum != 0 && float64(v) > f.Maximum {
		return errMaxReturn(f, float64(v))
	}
	if !f.inEnums(value) {
		return errEnumReturn(f, value)
	}
	return nil
}

func (f *FieldInfo) inEnums(v any) bool {
	if len(f.Enums) == 0 {
		return true
	}
	for i := 0; i < len(f.Enums); i++ {
		if f.enums[i] == v {
			return true
		}
	}
	return false
}
