package ii

import (
	"fmt"
	"reflect"
	"slices"
	"strconv"
	"strings"
	"time"

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

var (
	errCovertReturn = func(f *FieldInfo, v any) error {
		return fmt.Errorf("%s: %v not covert name: %s valueType: %v", getCallerName(), v, f.Name, valueType(v))
	}
	errCovertRetErr = func(f *FieldInfo, v any, err error) error {
		return fmt.Errorf("%s: %v not covert name: %s valueType: %s err: %s", getCallerName(), v, f.Name, valueType(v), err)
	}
)

// Convert 将 value 转换为 Type 类型. 遇到任何错误时返回
// value 被设计为传入非指针类型参数. 当前除 mo.TypeBinary 支持传入指针类型(用作反射代码示例), 其他 Type 都会返回错误
// 详情见 field_covert_test.go
func (f *FieldInfo) Convert(value any) (any, error) {
	switch f.Type {
	case mo.TypeDouble:
		return f.convertDouble(value)
	case mo.TypeString:
		return f.convertString(value)
	case mo.TypeObject:
		return f.convertObject(value)
	case mo.TypeArray:
		return f.convertArray(value)
	case mo.TypeBinary:
		return f.convertBinary(value)
	case mo.TypeObjectId:
		return f.convertObjectID(value)
	case mo.TypeBoolean:
		return f.convertBoolean(value)
	case mo.TypeDateTime:
		return f.convertDate(value)
	case mo.TypeInt32:
		return f.convertInt32(value)
	case mo.TypeInt64:
		return f.convertInt64(value)
	default:
		return nil, errCovertReturn(f, value)
	}
}

// ConvertWithValidate 转换数据并校验
func (f *FieldInfo) ConvertWithValidate(value any) (any, error) {
	v, err := f.Convert(value)
	if err != nil {
		return nil, err
	}
	if err = f.Validate(v); err != nil {
		return nil, fmt.Errorf("%s: %s", f.Name, err)
	}
	return v, nil
}

func (f *FieldInfo) convertDouble(value any) (float64, error) {
	switch v := value.(type) {
	case float64, float32, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		val := reflect.ValueOf(v).Convert(reflect.TypeOf(float64(0)))
		return toFloat64Decimal(val.Float(), f.Decimal), nil
	case string:
		if v == "" {
			return 0, errCovertReturn(f, value)
		}
		val, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return 0, errCovertRetErr(f, value, err)
		}
		return toFloat64Decimal(val, f.Decimal), nil
	default:
		return 0, errCovertReturn(f, value)
	}
}

func (f *FieldInfo) convertString(value any) (string, error) {
	if value == nil {
		return "", errNil
	}
	rv := reflect.ValueOf(value)
	switch rv.Type().Kind() {
	case reflect.String:
		if value == internalNew {
			return tuid.New(), nil
		}
		return rv.String(), nil
	case reflect.Bool:
		return strconv.FormatBool(rv.Bool()), nil
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(rv.Int(), 10), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(rv.Uint(), 10), nil
	case reflect.Float64, reflect.Float32:
		return strconv.FormatFloat(rv.Float(), 'g', -1, 64), nil
	case reflect.Map:
		val, err := f.convertObject(value)
		if err != nil {
			return "", errCovertReturn(f, value)
		}
		bv, err := mo.MarshalExtJSON(val, false, true)
		if err != nil {
			return "", err
		}
		return string(bv), nil
	case reflect.Array, reflect.Slice:
		length := rv.Len()
		val := make([]string, length)
		for i := 0; i < length; i++ {
			sv, err := f.convertString(rv.Index(i).Interface())
			if err != nil {
				return "", err
			}
			val[i] = sv
		}
		return strings.Join(val, ","), nil
	default:
		return "", errCovertReturn(f, value)
	}
}

// convertObject 将 value 转换为 mo.D 类型
// 对于已经是 map 类型的 value, 则需要深度 copy 后返回.
// 当大量转换时可能会出现性能影响
// 2023/01/28: from eric: object/map 类型的数据不允许 value 再次作为 map, 即只能存在一层 map // 移动至 ItemInfo 的 initFieldMap 中实现
func (f *FieldInfo) convertObject(value any) (mo.D, error) {
	var doc mo.D
	switch val := value.(type) {
	case mo.D:
		doc = val
	default:
		if err := mo.DecodeJson(val, &doc); err != nil {
			return nil, errCovertRetErr(f, value, err)
		}
	}
	if f.NoField {
		return doc, nil
	}
	obj := make(mo.D, 0)
	for _, field := range f.Fields {
		idx := slices.IndexFunc(doc, func(e mo.E) bool {
			return field.Name == e.Key
		})
		if idx == -1 {
			continue // 如果 XML 中未配置此字段
		}
		e := doc[idx]
		ev, err := field.Convert(e.Value)
		if err != nil {
			return nil, errCovertRetErr(f, value, err)
		}
		obj = append(obj, mo.E{Key: e.Key, Value: ev})
	}
	return obj, nil
}

func (f *FieldInfo) convertObjectWith(value, t any) error {
	m, err := f.convertObject(value)
	if err != nil {
		return err
	}
	b, err := mo.Marshal(m)
	if err != nil {
		return errCovertRetErr(f, value, err)
	}
	return mo.Unmarshal(b, t)
}

func (f *FieldInfo) convertArray(value any) (mo.A, error) {
	if value == nil {
		return nil, errNil
	}
	rv := reflect.ValueOf(value)
	switch rv.Type().Kind() {
	case reflect.Slice, reflect.Array:
		length := rv.Len()
		n := make(mo.A, length)
		for i := 0; i < length; i++ {
			rvi := rv.Index(i).Interface()
			if err := covertArray(f, rvi, &n, i); err != nil {
				return nil, err
			}
		}
		return n, nil
	case reflect.String:
		v := rv.String()
		if strings.TrimSpace(v) == "" {
			return mo.A{}, nil
		}
		if indexEqual(v, "{", "}") {
			n := make(mo.A, 1)
			if err := covertArray(f, v, &n, 0); err != nil {
				return nil, err
			}
			return n, nil
		}
		if indexEqual(v, "[", "]") {
			// 移除括号
			v = strings.TrimPrefix(v, "[")
			v = strings.TrimSuffix(v, "]")
			// 如果包含 , 表示数组内有多个元素
			if strings.Contains(v, ",") {
				str := strings.Split(v, ",")
				n := make(mo.A, len(str))
				for i, s := range str {
					if err := covertArray(f, s, &n, i); err != nil {
						return nil, err
					}
				}
				return n, nil
			} else {
				// 否则表示只有一个元素
				n := make(mo.A, 1)
				if err := covertArray(f, v, &n, 0); err != nil {
					return nil, err
				}
				return n, nil
			}
		} else {
			n := make(mo.A, 1)
			if err := covertArray(f, v, &n, 0); err == nil {
				return n, nil
			}
		}
		return nil, errCovertReturn(f, v)
	case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return mo.A{value}, nil
	case reflect.Map:
		// 兼容 javaScript 传入空数组时会被 Go 语言解析为 (interface{}|map[string]interface{}) 的问题
		return mo.A{}, nil
	default:
		return nil, errCovertReturn(f, value)
	}
}

// convertBinary
func (f *FieldInfo) convertBinary(value any) (mo.Binary, error) {
	if value == nil {
		return mo.Binary{}, errNil
	}
	rv := reflect.ValueOf(value)
	// 获取 value 的类型, 例如 pointer, int64, float64, map, slice, array
	switch rv.Type().Kind() {
	case reflect.Pointer:
		// 由于首先确认 value 是指针, 因此此处无需再次判断 CanSet
		// if rv.Elem().CanSet() {
		//
		// }
		// 当类型为 Pointer 时则需要使用 Elem() 函数操作
		rvp := rv.Type().Elem()
		// 所以 value 作为一个指针传入时, 先判断指针的类型是否为 Array 或 Slice
		if rvp.Kind() != reflect.Array && rvp.Kind() != reflect.Slice {
			return mo.Binary{}, errCovertReturn(f, value)
		}
		// 由于已知 rvp 为 Array 或 Slice 类型, 则再次调用 Elem() 函数表示获取其元素类型
		// 备忘录: 若数组内的元素数据类型不一致时则 Kind() 会返回 interface
		// uint8: [1,2,3,4,5]
		// interface: ["1", 3.14, []byte{0x01, 0x02}]
		if rvp.Elem().Kind() != reflect.Uint8 {
			return mo.Binary{}, errCovertReturn(f, value)
		}
		// 检查完毕指针内部的类型后, 应继续调用 rv 表示使用指针操作
		// 因此通过 rv.Elem() 调用 Bytes()
		return mo.Binary{Data: rv.Elem().Bytes()}, nil
	case reflect.Uint8:
		return mo.Binary{Data: []byte{uint8(rv.Uint())}}, nil
	case reflect.Slice, reflect.Array:
		if rv.Type().Elem().Kind() != reflect.Uint8 {
			return mo.Binary{}, errCovertReturn(f, value)
		}
		length := rv.Len()
		val := make([]byte, length)
		for i := 0; i < length; i++ {
			val[i] = rv.Index(i).Interface().(byte)
		}
		return mo.Binary{Data: val}, nil
	case reflect.String:
		val := gnet.String(rv.String()).Hex()
		if val == nil {
			return mo.Binary{}, errCovertReturn(f, value)
		}
		return mo.Binary{Data: val}, nil
	case reflect.Struct:
		val, ok := rv.Interface().(mo.Binary)
		if ok {
			if val.IsZero() {
				return mo.Binary{}, errCovertReturn(f, value)
			}
			return val, nil
		}
		fallthrough
	default:
		return mo.Binary{}, errCovertReturn(f, value)
	}
}

func (f *FieldInfo) convertObjectID(value any) (mo.ObjectID, error) {
	switch v := value.(type) {
	case mo.ObjectID:
		// 如果是 ObjectID 类型, 则直接返回, 不判断 v 是否为 Zero. 如果需要判断 v 是否为 Zero 时应调用 Validate 函数
		return v, nil
	case string:
		// 当 v 不等于空
		if v != "" {
			if v == internalNew {
				return mo.ID.New(), nil
			}
			if v == NilObjectId {
				return mo.NilObjectID, nil
			}
			val, err := mo.ID.From(v)
			if err != nil {
				return mo.NilObjectID, errCovertRetErr(f, val, err)
			}
			return val, nil
		} else {
			return mo.NilObjectID, nil
		}
	default:
		return mo.NilObjectID, errCovertReturn(f, value)
	}
}

func (f *FieldInfo) convertBoolean(value any) (bool, error) {
	switch v := value.(type) {
	case bool:
		return v, nil
	case string:
		if v == "" {
			return false, errCovertReturn(f, value)
		}
		val, err := strconv.ParseBool(v)
		if err != nil {
			return false, errCovertRetErr(f, value, err)
		}
		return val, nil
	case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, float32, float64:
		val := reflect.ValueOf(v).Convert(reflect.TypeOf(int64(0)))
		return val.Int() == 1, nil
	default:
		return false, errCovertReturn(f, value)
	}
}

// convertDate 将 value 转换为 mo.DateTime 类型
// convertDate 函数会将 value 用做 毫秒 进行解析
func (f *FieldInfo) convertDate(value any) (mo.DateTime, error) {
	switch v := value.(type) {
	case mo.DateTime:
		return mo.NewDateTimeFromTime(v.Time().Local()), nil
	case time.Time:
		return mo.NewDateTimeFromTime(v.Local()), nil
	case time.Duration:
		// 时间数字类型使用 纳秒 单位
		return mo.NewDateTimeFromTime(time.Unix(0, v.Nanoseconds()).Local()), nil
	case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, float32, float64:
		val := reflect.ValueOf(v).Convert(reflect.TypeOf(int64(0)))
		// 如果是数字类型, 则使用 秒 单位
		return mo.NewDateTimeFromTime(time.Unix(val.Int(), 0).Local()), nil
	case string:
		if v == "" {
			return 0, errCovertReturn(f, value)
		}
		if v == internalNow {
			return mo.NewDateTime(), nil
		}
		var tim time.Time
		if err := tim.UnmarshalText([]byte(v)); err != nil {
			return 0, errCovertRetErr(f, value, err)
		}
		return mo.NewDateTimeFromTime(tim.Local()), nil
	case []byte:
		if val := gnet.BigEndian.Uint64(v); val > 0 {
			return mo.NewDateTimeFromTime(time.Unix(int64(val), 0).Local()), nil
		}
		if val := gnet.LittleEndian.Uint64(v); val > 0 {
			return mo.NewDateTimeFromTime(time.Unix(int64(val), 0).Local()), nil
		}
		return 0, errCovertReturn(f, value)
	default:
		return 0, errCovertReturn(f, value)
	}
}

func (f *FieldInfo) convertInt32(value any) (int32, error) {
	switch v := value.(type) {
	case int32:
		return v, nil
	case int64:
		return int32(v), nil
	case float64:
		return int32(v), nil
	case uint64:
		return int32(v), nil
	case int:
		return int32(v), nil
	case uint:
		return int32(v), nil
	case uint8:
		return int32(v), nil
	case uint16:
		return int32(v), nil
	case uint32:
		return int32(v), nil
	case int8:
		return int32(v), nil
	case int16:
		return int32(v), nil
	case float32:
		return int32(v), nil
	case string:
		if v == "" {
			return 0, errCovertReturn(f, value)
		}
		val, err := strconv.ParseInt(v, 10, 32)
		if err != nil {
			return 0, errCovertRetErr(f, val, err)
		}
		return int32(val), nil
	case []byte:
		if val := gnet.BigEndian.Int32(v); val > 0 {
			return val, nil
		}
		return 0, errCovertReturn(f, value)
	case time.Duration:
		return int32(v.Milliseconds()), nil
	case time.Time:
		return int32(v.UnixMilli()), nil
	case mo.DateTime:
		return int32(v.Time().UnixMilli()), nil
	default:
		return 0, errCovertReturn(f, value)
	}
}

func (f *FieldInfo) convertInt64(value any) (int64, error) {
	switch v := value.(type) {
	case int64:
		return v, nil
	case float64:
		return int64(v), nil
	case uint64:
		return int64(v), nil
	case int:
		return int64(v), nil
	case uint:
		return int64(v), nil
	case uint8:
		return int64(v), nil
	case uint16:
		return int64(v), nil
	case uint32:
		return int64(v), nil
	case int8:
		return int64(v), nil
	case int16:
		return int64(v), nil
	case int32:
		return int64(v), nil
	case float32:
		return int64(v), nil
	case string:
		if v == "" {
			return 0, errCovertReturn(f, value)
		}
		val, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return 0, errCovertRetErr(f, val, err)
		}
		return val, nil
	case []byte:
		if val := gnet.BigEndian.Int64(v); val > 0 {
			return val, nil
		}
		return 0, errCovertReturn(f, value)
	case time.Duration:
		return v.Milliseconds(), nil
	case time.Time:
		return v.UnixMilli(), nil
	case mo.DateTime:
		return v.Time().UnixMilli(), nil
	default:
		return 0, errCovertReturn(f, value)
	}
}
