package map2struct

import (
	"github.com/mitchellh/mapstructure"
	"github.com/shopspring/decimal"
	"github.com/spf13/cast"
	"gopkg.in/guregu/null.v4"
	"reflect"
	"time"
)

var timeLocation *time.Location

func init() {
	timeLocation, _ = time.LoadLocation("Asia/Shanghai")
}

func ToTimeHookFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if t != reflect.TypeOf(time.Time{}) {
			return data, nil
		}

		switch f.Kind() {
		case reflect.String:
			return cast.ToTimeInDefaultLocationE(data, timeLocation)
		case reflect.Float64:
			return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
		case reflect.Int64:
			return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
		default:
			return data, nil
		}
		// Convert it by parsing
	}
}

func ToTimePtrHookFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if t != reflect.TypeOf(&time.Time{}) {
			return data, nil
		}

		switch f.Kind() {
		case reflect.String:
			if data == "" {
				return nil, nil
			}
			v, err := cast.ToTimeInDefaultLocationE(data, timeLocation)
			return &v, err
		case reflect.Float64:
			v := time.Unix(0, int64(data.(float64))*int64(time.Millisecond))
			return &v, nil
		case reflect.Int64:
			v := time.Unix(0, data.(int64)*int64(time.Millisecond))
			return &v, nil
		default:
			return data, nil
		}
		// Convert it by parsing
	}
}

func ToDecimalHookFunc() mapstructure.DecodeHookFunc {
	return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {

		if t == reflect.TypeOf(decimal.Decimal{}) {
			if f.Kind() == reflect.Float64 {
				return decimal.NewFromFloat(data.(float64)), nil
			}

			if input := data.(string); input != "" {
				return decimal.NewFromString(data.(string))
			}
			return decimal.Decimal{}, nil
		}

		return data, nil
	}
}

func ToNullableStringHookFunc() mapstructure.DecodeHookFunc {
	return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
		if t == reflect.TypeOf(null.String{}) {
			return null.StringFrom(data.(string)), nil
		}

		return data, nil
	}
}
