package utils

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"git.rosy.net.cn/baseapi"
	"github.com/gazeboxu/mapstructure"
	"github.com/gazeboxu/structs"
	jsoniter "github.com/json-iterator/go"
)

var json1 = jsoniter.ConfigCompatibleWithStandardLibrary

const MaxTimeSecond = 9573800254 // 正常最大的秒数

var (
	DefaultTimeValue      = Str2Time("1970-01-01 00:00:00")
	WayBillDeliveryMarkUp = 20 // 运单加价两毛钱
	ZeroTimeValue         = time.Time{}
)

func UnmarshalUseNumber(data []byte, result interface{}) error {
	err := TryUnmarshalUseNumber(data, result)
	if err != nil {
		baseapi.SugarLogger.Infof("decode data:%v, error:%v", string(data), err)
	}
	return err
}

func TryUnmarshalUseNumber(data []byte, result interface{}) error {
	d := json.NewDecoder(bytes.NewReader(data))
	d.UseNumber()
	return d.Decode(result)
}

// 这个函数将解析json，返回的map中的字段类型与structObj中的完全一样的
func Unmarshal2Map(data []byte, structObj interface{}) (resultMap map[string]interface{}, err error) {
	if err = json.Unmarshal(data, structObj); err == nil {
		if err = json.Unmarshal(data, &resultMap); err == nil {
			m := Struct2MapByJson(structObj)
			for k := range resultMap {
				if value, ok := m[k]; ok {
					resultMap[k] = value
				} else {
					delete(resultMap, k)
				}
			}
		}
	}
	return resultMap, err
}

func MustMarshal(obj interface{}) []byte {
	byteArr, err := json.Marshal(obj)
	if err != nil {
		panic(fmt.Sprintf("err when Marshal obj:%v with error:%v", obj, err))
	}
	return byteArr
}

func TryInterface2Int64(data interface{}) (num int64, err error) {
	if data == nil {
		return num, errors.New("data is nil")
	}
	if dataNumber, ok := data.(int64); ok {
		return dataNumber, nil
	}
	if dataNumber, ok := data.(int); ok {
		return int64(dataNumber), nil
	}
	if dataNumber, ok := data.(int32); ok {
		return int64(dataNumber), nil
	}
	if dataNumber, ok := data.(int16); ok {
		return int64(dataNumber), nil
	}
	if dataNumber, ok := data.(int8); ok {
		return int64(dataNumber), nil
	}
	if dataNumber, ok := data.(json.Number); ok {
		return dataNumber.Int64()
	}
	if str, ok := data.(string); ok {
		return Str2Int64WithDefault(str, 0), nil
	}
	return 0, fmt.Errorf("data is not json.Number, it's %s, value:%v", reflect.TypeOf(data).String(), data)
}

func MustInterface2Int64(data interface{}) int64 {
	retVal, err := TryInterface2Int64(data)
	if err != nil {
		panic(err.Error())
	}
	return retVal
}

func Interface2Int64WithDefault(data interface{}, defValue int64) int64 {
	retVal, err := TryInterface2Int64(data)
	if err != nil {
		return defValue
	}
	return retVal
}

func ForceInterface2Int64(data interface{}) int64 {
	if dataStr, ok := data.(string); ok {
		return Str2Int64(dataStr)
	}
	return Interface2Int64WithDefault(data, 0)
}

func TryInterface2Float64(data interface{}) (num float64, err error) {
	if data == nil {
		return num, errors.New("data is nil")
	}
	if dataNumber, ok := data.(float64); ok {
		return dataNumber, nil
	}
	if dataNumber, ok := data.(float32); ok {
		return float64(dataNumber), nil
	}
	if dataNumber, ok := data.(json.Number); ok {
		return dataNumber.Float64()
	}
	if str, ok := data.(string); ok {
		return Str2Float64WithDefault(str, 0), nil
	}
	return 0, fmt.Errorf("data is not json.Number, it's %s, value:%v", reflect.TypeOf(data).String(), data)
}

func MustInterface2Float64(data interface{}) float64 {
	retVal, err := TryInterface2Float64(data)
	if err != nil {
		panic(err.Error())
	}
	return retVal
}

func Interface2Float64WithDefault(data interface{}, defValue float64) (retVal float64) {
	retVal, err := TryInterface2Float64(data)
	if err != nil {
		return defValue
	}
	return retVal
}

func Interface2String(data interface{}) string {
	if data == nil {
		return ""
	}
	return data.(string)
}

func Interface2Int64List(data interface{}) []int64 {
	if data == nil {
		return nil
	}
	list := data.([]interface{})
	retVal := make([]int64, len(list))
	for k, v := range list {
		retVal[k] = MustInterface2Int64(v)
	}
	return retVal
}

func Interface2StringList(data interface{}) []string {
	if data == nil {
		return nil
	}
	list := data.([]interface{})
	retVal := make([]string, len(list))
	for k, v := range list {
		retVal[k] = Interface2String(v)
	}
	return retVal
}

func Slice2MapSlice(data []interface{}) []map[string]interface{} {
	retVal := make([]map[string]interface{}, len(data))
	for k, v := range data {
		retVal[k] = v.(map[string]interface{})
	}
	return retVal
}

func Slice2int64Slice(data []interface{}) []int64 {
	retVal := make([]int64, len(data))
	for k, v := range data {
		retVal[k] = MustInterface2Int64(v)
	}
	return retVal
}

func Slice2StringSlice(data []interface{}) []string {
	retVal := make([]string, len(data))
	for k, v := range data {
		retVal[k] = Interface2String(v)
	}
	return retVal
}

//
func Bool2String(value bool) string {
	if value {
		return "true"
	}
	return "false"
}

func Bool2Int(value bool) int {
	if value {
		return 1
	}
	return 0
}

func Bool2Point(value bool) *bool {
	return &value
}

func Str2Int64WithDefault(str string, defValue int64) int64 {
	retVal, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return defValue
	}
	return retVal
}

func Str2Int64(str string) int64 {
	retVal, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		baseapi.SugarLogger.Errorf("error when convert %s to int64", str)
	}
	return retVal
}

func Str2Float64WithDefault(str string, defValue float64) float64 {
	retVal, err := strconv.ParseFloat(str, 64)
	if err != nil {
		retVal = defValue
	}
	return retVal
}

func Str2Float64(str string) float64 {
	retVal, err := strconv.ParseFloat(str, 64)
	if err != nil {
		baseapi.SugarLogger.Errorf("error when convert %s to float64", str)
	}
	return retVal
}

func Int64ToStr(value int64) string {
	return strconv.FormatInt(value, 10)
}

func Int64ToStrNoZero(value int64) string {
	if value == 0 {
		return ""
	}
	return strconv.FormatInt(value, 10)
}

func Int2Str(value int) string {
	return strconv.Itoa(value)
}

func Float64TwoInt64(data float64) int64 {
	return int64(math.Round(data))
}

func Float64ToStr(data float64) string {
	return fmt.Sprint(data)
}

func Float32ToStr(data float32) string {
	return fmt.Sprint(data)
}

// timestamp is in second
func Timestamp2Str(timestamp int64) string {
	return Time2Str(Timestamp2Time(timestamp))
}

// timestamp为秒或毫秒
func Timestamp2Time(timestamp int64) time.Time {
	nsec := int64(0)
	if timestamp > MaxTimeSecond { // 传成毫秒了
		nsec = (timestamp % 1000) * 1000000
		timestamp /= 1000
	}
	return time.Unix(timestamp, nsec)
}

func Time2Str(t time.Time) string {
	return t.Format("2006-01-02 15:04:05")
}

func Time2TimeStr(t time.Time) string {
	return t.Format("15:04:05")
}
func Time2TimeStrByFormat(t time.Time, format string) string {
	return t.Format(format)
}

func Time2Date(t time.Time) time.Time {
	year, month, day := t.Date()
	return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
}

func Time2DateStr(t time.Time) string {
	return t.Format("2006-01-02")
}

func Str2Time(timeStr string) time.Time {
	if timeStr == "" {
		return ZeroTimeValue
	}
	retVal, err := TryStr2Time(timeStr)
	if err != nil {
		baseapi.SugarLogger.Errorf("time.ParseInLocation failed, timeStr:%v, error:%v", timeStr, err)
	}
	return retVal
}

func TryStr2Time(timeStr string) (time.Time, error) {
	timeStr = strings.Replace(timeStr, "T", " ", 1)
	if strings.Index(timeStr, " ") == -1 {
		timeStr += " 00:00:00"
	}
	maxTimeStrLen := len("2018-05-03 09:18:40")
	if len(timeStr) > maxTimeStrLen {
		timeStr = timeStr[:maxTimeStrLen]
	}
	return time.ParseInLocation("2006-1-2 15:4:5", timeStr, time.Local)
}

func Str2TimeWithDefault(timeStr string, defValue time.Time) time.Time {
	retVal, err := TryStr2Time(timeStr)
	if err != nil {
		return defValue
	}
	return retVal
}

func IsTimeZero(timeValue time.Time) bool {
	return timeValue == DefaultTimeValue || timeValue == ZeroTimeValue
}

func IsPtrTimeZero(timePtr *time.Time) bool {
	return timePtr == nil || *timePtr == DefaultTimeValue || *timePtr == ZeroTimeValue
}

func HTTPBody2Values(data []byte, needDecode bool) (url.Values, error) {
	bodyStr := string(data)
	if needDecode {
		bodyStr1, err := url.QueryUnescape(bodyStr)
		if err != nil {
			baseapi.SugarLogger.Errorf("QueryUnescape error:%v, bodyStr:%v", err, bodyStr)
			return nil, err
		}
		bodyStr = bodyStr1
	}
	result, err := url.ParseQuery(bodyStr)
	if err != nil {
		baseapi.SugarLogger.Errorf("ParseQuery error:%v, bodyStr:%v", err, bodyStr)
		return nil, err
	}
	return result, nil
}

func Params2Map(key1, value1 interface{}, kv ...interface{}) (retVal map[string]interface{}) {
	retVal = make(map[string]interface{})
	retVal[key1.(string)] = value1

	key := ""
	for index, v := range kv {
		if index%2 == 0 {
			key = v.(string)
		} else {
			retVal[key] = v
		}
	}

	return retVal
}

func URLValues2Map(values url.Values) (retVal map[string]interface{}) {
	retVal = make(map[string]interface{})
	for k := range values {
		retVal[k], _ = url.QueryUnescape(values.Get(k))
	}
	return retVal
}

func Map2URLValues(mapData map[string]interface{}) (retVal url.Values) {
	retVal = make(url.Values)
	for k, v := range mapData {
		retVal.Set(k, fmt.Sprint(v))
	}
	return retVal
}

func Map2URLValues2(mapData map[string]interface{}) (retVal url.Values) {
	retVal = make(url.Values)
	for k, v := range mapData {
		if data, ok := v.(string); ok {
			retVal.Set(k, data)
		} else {
			retVal.Set(k, fmt.Sprint(v))
		}
	}
	return retVal
}

func MapKV2List(mapData map[string]interface{}) []map[string]interface{} {
	retVal := make([]map[string]interface{}, len(mapData))
	index := 0
	for _, v := range mapData {
		retVal[index] = v.(map[string]interface{})
		index++
	}
	return retVal
}

func MapKeys(mapData map[string]interface{}) (keys []string) {
	for k := range mapData {
		keys = append(keys, k)
	}
	return keys
}

func Format4Output(obj interface{}, isSingleLine bool) (retVal string) {
	retVal, ok := obj.(string)
	if !ok {
		var (
			result []byte
			err    error
		)
		if isSingleLine {
			if result, err = json.Marshal(obj); err == nil {
				retVal = string(result)
			}
		} else {
			if result, err = json.MarshalIndent(obj, "", "\t"); err == nil {
				retVal = string(result)
			}
		}
		if err != nil {
			baseapi.SugarLogger.Infof("Format4Output Marshal:%v failed with error:%v", obj, err)
			retVal = fmt.Sprintf("%v", obj)
		}
	}
	return retVal
}

func Map2KeySlice(flagMap map[string]int) (keyList []string) {
	for k, v := range flagMap {
		if v != 0 {
			keyList = append(keyList, k)
		}
	}
	return keyList
}

// 合并map，相同字段以前面的优先，后来相同的字段忽略
func MergeMaps(firstMap map[string]interface{}, otherMaps ...map[string]interface{}) (retVal map[string]interface{}) {
	retVal = make(map[string]interface{})
	allMaps := append([]map[string]interface{}{firstMap}, otherMaps...)
	mapCount := len(allMaps)
	for index := range allMaps {
		oneMap := allMaps[mapCount-index-1]
		for k, v := range oneMap {
			retVal[k] = v
		}
	}
	return retVal
}

func Struct2Map(obj interface{}, tagName string, isFlattenAnonymous bool) (mapData map[string]interface{}) {
	if tagName == "" {
		tagName = "json"
	}
	structsObj := structs.New(obj)
	structsObj.TagName = tagName
	structsObj.IsFlattenAnonymous = true
	return structsObj.Map()
}

func Struct2MapByJson(obj interface{}) (mapData map[string]interface{}) {
	return Struct2Map(obj, "", false)
}

func Struct2FlatMap(obj interface{}) map[string]interface{} {
	return Struct2Map(obj, "", true)
}

// !!! 此函数好像不支持struct是内嵌结构的
func Map2Struct(inObj interface{}, outObjAddr interface{}, weaklyTypedInput bool, tagName string, decodeHook interface{}) (err error) {
	if tagName == "" {
		tagName = "json"
	}
	if !weaklyTypedInput {
		weaklyTypedInput = decodeHook != nil
	}
	decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		TagName:          tagName,
		Result:           outObjAddr,
		WeaklyTypedInput: weaklyTypedInput,
		DecodeHook:       decodeHook,
	})
	return decoder.Decode(inObj)
}

func Map2StructByJson(inObj interface{}, outObjAddr interface{}, weaklyTypedInput bool) (err error) {
	return Map2Struct(inObj, outObjAddr, weaklyTypedInput, "", nil)
}

func Int64Slice2String(intList []int64) (outList []string) {
	if len(intList) > 0 {
		outList = make([]string, len(intList))
		for k, v := range intList {
			outList[k] = Int64ToStr(v)
		}
	}
	return outList
}

func StringSlice2Int64(intList []string) (outList []int64) {
	if len(intList) > 0 {
		outList = make([]int64, len(intList))
		for k, v := range intList {
			outList[k] = Str2Int64WithDefault(v, 0)
		}
	}
	return outList
}

func StringSlice2Int(intList []string) (outList []int) {
	if len(intList) > 0 {
		outList = make([]int, len(intList))
		for k, v := range intList {
			outList[k] = int(Str2Int64WithDefault(v, 0))
		}
	}
	return outList
}

func IntSlice2Int64(intList []int) (outList []int64) {
	if len(intList) > 0 {
		outList = make([]int64, len(intList))
		for k, v := range intList {
			outList[k] = int64(v)
		}
	}
	return outList
}

func Int2Float64(i int) (f float64) {
	return Str2Float64(Int64ToStr(int64(i)))
}

func Float32ToInt(f float32) (i int) {
	return int(Str2Int64(Float64ToStr(math.Round(float64(f)))))
}

func Str2Int(s string) (i int) {
	return int(Str2Int64WithDefault(s, 0))
}

func Str2Int16(s string) (i int16) {
	return int16(Str2Int64WithDefault(s, 0))
}

//func String2Int64Slice(s string) []int64 {
//	res := make([]int64, 0)
//	if len(s) == 0 {
//		return res
//	}
//	if err := json1.DecoderOf(s, &res); err != nil {
//		log.Warnf("Error to deserialize string of []int64:%s, err=%v", s, err)
//		return res
//	}
//	return res
//}

func Float64TwoInt(f float64) (i int) {
	return int(Float64TwoInt64(f))
}

func URLQueryEscape(s string) (str string) {
	str, _ = url.QueryUnescape(s)
	return str
}

func Int64ToFloat64(i int64) (f float64) {
	return Str2Float64(Int64ToStr(i))
}
