package refutil

import (
	"bytes"
	"encoding/base64"
	"encoding/gob"
	"fmt"
	"reflect"

	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/globals"
)

func CheckAndGetStructValue(item interface{}) *reflect.Value {
	value := reflect.ValueOf(item)
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	} else {
		panic("item ust be ptr type")
	}
	return &value
}

func IsFieldExist(obj interface{}, fieldName string) bool {
	return reflect.Indirect(reflect.ValueOf(obj)).FieldByName(fieldName).IsValid()
}

func GetObjFieldByName(obj interface{}, fieldName string) interface{} {
	return reflect.Indirect(reflect.ValueOf(obj)).FieldByName(fieldName).Interface()
}

func SetObjFieldByName(obj interface{}, fieldName string, value interface{}) {
	refValue := CheckAndGetStructValue(obj)
	refValue.FieldByName(fieldName).Set(reflect.ValueOf(value))
}

func SerializeData(data interface{}) (strValue string, err error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err = enc.Encode(data); err == nil {
		strValue = base64.StdEncoding.EncodeToString(buf.Bytes())
		return strValue, nil
	}
	return "", err
}

func DeSerializeData(strValue string, dataPtr interface{}) (err error) {
	byteData, err := base64.StdEncoding.DecodeString(strValue)
	if err == nil {
		dec := gob.NewDecoder(bytes.NewReader(byteData))
		return dec.Decode(dataPtr)
	}
	return err
}

// mapData 参数 obj 对象
// todo 这里看是否需要将key值转换成标准格式（即字母大写），因为beego orm不区分，不转换也可以
func FilterMapByStructObject(mapData map[string]interface{}, obj interface{}, objPtr interface{}, excludedFields []string, isCheckValue bool) (valid map[string]interface{}, invalid map[string]interface{}) {
	excludedMap := make(map[string]int)
	for _, v := range excludedFields {
		excludedMap[v] = 1
	}
	m := utils.Struct2FlatMap(obj)
	valid = make(map[string]interface{})
	invalid = make(map[string]interface{})
	for k, v := range mapData {
		if m[k] != nil && excludedMap[k] == 0 /*&& v != nil*/ && (!isCheckValue || !IsValueEqual(m[k], v)) {
			valid[k] = v
			m[k] = v
		} else {
			invalid[k] = v
		}
	}
	if objPtr != nil {
		if err := utils.Map2StructByJson(m, objPtr, true); err != nil {
			globals.SugarLogger.Warnf("FilterMapByStructObject failed with err:%v, mapData:%s", err, utils.Format4Output(mapData, true))
		}
	}

	return valid, invalid
}

func FilterMapByFieldList(mapData map[string]interface{}, fields []string) (valid map[string]interface{}, invalid map[string]interface{}) {
	valid = make(map[string]interface{})
	invalid = make(map[string]interface{})
	for _, field := range fields {
		if mapData[field] != nil {
			valid[field] = mapData[field]
		} else {
			invalid[field] = mapData[field]
		}
	}
	return valid, invalid
}

func FindMapAndStructMixed(mapData map[string]interface{}, obj interface{}) (valid map[string]interface{}) {
	valid = make(map[string]interface{})
	map2 := utils.Struct2FlatMap(obj)
	for k, v := range map2 {
		if mapData[k] != nil {
			valid[k] = v
		}
	}
	return valid
}

func IsValueEqual(value1, value2 interface{}) bool {
	if value1 == nil || value2 == nil {
		return value1 == value2
	}
	return Interface2String(value1) == Interface2String(value2)
}

func Interface2String(value interface{}) (str string) {
	valueType := reflect.TypeOf(value)
	if valueType.Kind() == reflect.Ptr {
		value = reflect.ValueOf(value).Elem()
	}
	str = fmt.Sprint(value)
	return str
}
