package hash

import (
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"sort"
)

func JsonHashKey(v interface{}) string {

	if _, ok := v.(string); ok {
		var obj interface{}
		err := json.Unmarshal([]byte(v.(string)), &obj)
		if err == nil {
			// 反序列化成功,直接替换v
			v = obj
		}
	}

	h := sha256.New()
	h.Write(marshalOrdered(v))
	return fmt.Sprintf("%x", h.Sum(nil))
}

func marshalOrdered(v interface{}) []byte {
	switch v := v.(type) {
	case map[string]interface{}:
		sortedKeys := make([]string, 0, len(v))
		for key := range v {
			sortedKeys = append(sortedKeys, key)
		}
		sort.Strings(sortedKeys)

		var buf bytes.Buffer
		buf.WriteByte('{')
		for i, key := range sortedKeys {
			if i > 0 {
				buf.WriteByte(',')
			}
			b, err := json.Marshal(key)
			if err != nil {
				panic(err)
			}
			buf.Write(b)
			buf.WriteByte(':')

			b = marshalOrdered(v[key])
			buf.Write(b)
		}
		buf.WriteByte('}')
		return buf.Bytes()

	case []interface{}:
		var buf bytes.Buffer

		sort.Slice(v, func(i, j int) bool {
			return bytes.Compare(marshalOrdered(v[i]), marshalOrdered(v[j])) == 1
		})
		buf.WriteByte('[')
		for i, val := range v {
			if i > 0 {
				buf.WriteByte(',')
			}

			b := marshalOrdered(val)
			buf.Write(b)
		}
		buf.WriteByte(']')
		return buf.Bytes()

	default:
		b, err := json.Marshal(v)
		if err != nil {
			panic(err)
		}
		return b
	}
}