package tool

import (
	"crypto/aes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/elliotchance/phpserialize"
)

const (
	// RpcUserKey 用户key
	RpcUserKey = `rpc-user`
)

type GalaxyMiniEncrypter struct {
	Serialize     bool
	Key           string
	DecrypterData GalaxyMiniDecrypterResult
}

type GalaxyMiniPayload struct {
	Iv    string `json:"iv"`
	Value string `json:"value"`
	Mac   string `json:"mac"`
}
type GalaxyMiniDecrypterResult struct {
	UserID          int64  `json:"user_id"`
	UserMobile      string `json:"user_mobile"`
	UserAppID       int64  `json:"user_app_id"`
	UserAppMobile   string `json:"user_app_mobile"`
	AppID           int64  `json:"app_id"`
	Timestamp       int64  `json:"timestamp"`
	Nickname        string `json:"nickname"`
	Uuid            string `json:"uuid"`
	AvatarUrl       string `json:"avatar_url"`
	Gender          string `json:"gender"`
	City            string `json:"city"`
	Province        string `json:"province"`
	Country         string `json:"country"`
	CreatedAt       string `json:"created_at"`
	UpdatedAt       string `json:"updated_at"`
	IsNewAppUser    int64  `json:"is_new_app_user"`
	IsWechatAppUser int64  `json:"is_wechat_app_user"`
	MarkUserAdmin   bool   `json:"mark_user_admin"`
	Session         struct {
		SessionKey string `json:"session_key"`
		Openid     string `json:"openid"`
		Unionid    string `json:"unionid"`
	} `json:"session"`
	Channel        string                  `json:"channel"`
	Env            string                  `json:"env"`
	CustomerId     int64                   `json:"customer_id"`
	CustomerExtIDs *[]int64                `json:"customer_ext_ids"`
	CustomerExtMap *map[int64]*CustomerExt `json:"customer_ext_map"`
}

// CustomerExt 协同客户数据
type CustomerExt struct {
	CustomerID int64  `json:"customer_id"`
	ID         int64  `json:"id"`
	Mobile     string `json:"mobile"`
	CreatedAt  string `json:"created_at"`
	UpdatedAt  string `json:"updated_at"`
	DeletedAt  string `json:"deleted_at"`
}

func (g *GalaxyMiniEncrypter) GalaxyEncrypt(data []byte) ([]byte, error) {
	mp := make(map[string]interface{})
	err := json.Unmarshal(data, &mp)
	if g.Serialize {
		data, err = phpserialize.Marshal(mp, nil)
	}
	keyByte := make([]byte, 32)
	copy(keyByte, g.Key)
	// 生成随机IV
	iv := make([]byte, aes.BlockSize)
	// 从随机源中读取随机字节填充IV
	_, err = rand.Read(iv)
	if err != nil {
		return nil, err
	}
	cipherText, err := Encrypt(string(data), keyByte, iv)
	if err != nil {
		return nil, err
	}
	key := base64.StdEncoding.EncodeToString(iv)
	h := hmac.New(sha256.New, keyByte)
	h.Write([]byte(key + cipherText))
	// 计算哈希值
	hashed := h.Sum(nil)
	// 将HMAC值转换为十六进制字符串
	hexResult := hex.EncodeToString(hashed)
	fmt.Printf("key: %s\n, cipherText: %s\n, hexResult: %s\n ,g.key: %s\n", key, cipherText, hexResult, g.Key)
	realData := &GalaxyMiniPayload{
		Iv:    key,
		Value: cipherText,
		Mac:   hexResult,
	}
	realBytes, _ := json.Marshal(realData)
	return []byte(base64.StdEncoding.EncodeToString(realBytes)), nil
}

func (g *GalaxyMiniEncrypter) MapToStruct(mp map[interface{}]interface{}) {
	for key, value := range mp {
		switch key {
		case "user_id":
			if v, ok := value.(int64); ok {
				g.DecrypterData.UserID = v
			}
		case "user_mobile":
			if v, ok := value.(string); ok {
				g.DecrypterData.UserMobile = v
			}
		case "user_app_id":
			if v, ok := value.(int64); ok {
				g.DecrypterData.UserAppID = v
			}
		case "user_app_mobile":
			if v, ok := value.(string); ok {
				g.DecrypterData.UserAppMobile = v
			}
		case "app_id":
			if v, ok := value.(int64); ok {
				g.DecrypterData.AppID = v
			}
		case "timestamp":
			if v, ok := value.(int64); ok {
				g.DecrypterData.Timestamp = v
			}
		case "nickname":
			if v, ok := value.(string); ok {
				g.DecrypterData.Nickname = v
			}
		case "uuid":
			if v, ok := value.(string); ok {
				g.DecrypterData.Uuid = v
			}
		case "avatar_url":
			if v, ok := value.(string); ok {
				g.DecrypterData.AvatarUrl = v
			}
		case "gender":
			if v, ok := value.(string); ok {
				g.DecrypterData.Gender = v
			}
		case "city":
			if v, ok := value.(string); ok {
				g.DecrypterData.City = v
			}
		case "province":
			if v, ok := value.(string); ok {
				g.DecrypterData.Province = v
			}
		case "country":
			if v, ok := value.(string); ok {
				g.DecrypterData.Country = v
			}
		case "created_at":
			if v, ok := value.(string); ok {
				g.DecrypterData.CreatedAt = v
			}
		case "updated_at":
			if v, ok := value.(string); ok {
				g.DecrypterData.UpdatedAt = v
			}
		case "is_new_app_user":
			if v, ok := value.(int64); ok {
				g.DecrypterData.IsNewAppUser = v
			}
		case "is_wechat_app_user":
			if v, ok := value.(int64); ok {
				g.DecrypterData.IsWechatAppUser = v
			}
		case "mark_user_admin":
			if v, ok := value.(bool); ok {
				g.DecrypterData.MarkUserAdmin = v
			}
		case "session":
			if v, ok := value.(map[interface{}]interface{}); ok {
				session := struct {
					SessionKey string `json:"session_key"`
					Openid     string `json:"openid"`
					Unionid    string `json:"unionid"`
				}{}
				session.SessionKey = v["session_key"].(string)
				session.Openid = v["openid"].(string)
				session.Unionid = v["unionid"].(string)
				g.DecrypterData.Session = session
			}
		case "channel":
			if v, ok := value.(string); ok {
				g.DecrypterData.Channel = v
			}
		case "env":
			if v, ok := value.(string); ok {
				g.DecrypterData.Env = v
			}
		}
	}
}

// GalaxyDecrypt rpc小程序解token
func (g *GalaxyMiniEncrypter) GalaxyDecrypt(data []byte) ([]byte, error) {
	var err error
	payload, err := g.getJsonPayload(data)
	if err != nil {
		return nil, err
	}
	keyByte := make([]byte, 32)
	copy(keyByte, g.Key)
	baseIv, err := base64.StdEncoding.DecodeString(payload.Iv)
	if err != nil {
		return nil, err
	}
	// aes 解密
	decryptData, err := Decrypt(payload.Value, keyByte, baseIv)
	// 反序列化
	if g.Serialize == true {
		unSerializeData, _ := phpserialize.UnmarshalAssociativeArray([]byte(decryptData))
		g.MapToStruct(unSerializeData)
	} else {
		err = json.Unmarshal([]byte(decryptData), &g.DecrypterData)
		if err != nil {
			return nil, err
		}
	}
	return json.Marshal(g.DecrypterData)
}

func (g *GalaxyMiniEncrypter) getJsonPayload(data []byte) (*GalaxyMiniPayload, error) {
	baseData, err := base64.StdEncoding.DecodeString(string(data))
	if err != nil {
		return nil, err
	}
	var payload *GalaxyMiniPayload
	err = json.Unmarshal(baseData, &payload)

	if err != nil {
		return nil, err
	}

	err = g.validPayload(payload)
	if err != nil {
		return nil, err
	}

	if g.validMac(payload) == false {
		return nil, errors.New("invalid mac")
	}
	return payload, nil
}

func (g *GalaxyMiniEncrypter) validPayload(payload *GalaxyMiniPayload) error {
	if payload.Iv == "" {
		return errors.New("iv is empty")
	}
	if payload.Value == "" {
		return errors.New("value is empty")
	}
	if payload.Mac == "" {
		return errors.New("mac is empty")
	}

	ivBase64, _ := base64.StdEncoding.DecodeString(payload.Iv)
	if len(ivBase64) != aes.BlockSize {
		return errors.New(`payload is invalid`)
	}

	return nil
}

func (g *GalaxyMiniEncrypter) validMac(payload *GalaxyMiniPayload) bool {
	// 从随机源中读取随机字节填充
	bytes := make([]byte, aes.BlockSize)
	_, _ = rand.Read(bytes)

	h := hmac.New(sha256.New, bytes)
	h.Write([]byte(payload.Mac))
	hashed := h.Sum(nil)

	calculated := g.calculateMac(payload, bytes)

	equal := hmac.Equal(hashed, calculated)
	fmt.Printf("hmac: %x\n, hashed: %x\n equal: %t", calculated, hashed, equal)

	return equal
}

func (g *GalaxyMiniEncrypter) calculateMac(payload *GalaxyMiniPayload, bytes []byte) []byte {
	h := hmac.New(sha256.New, bytes)
	h.Write(g.hash(payload.Iv, payload.Value))
	// 计算哈希值
	return h.Sum(nil)
}

func (g *GalaxyMiniEncrypter) hash(iv, value string) []byte {
	h := hmac.New(sha256.New, []byte(g.Key))
	h.Write([]byte(iv + value))
	// 计算哈希值
	return []byte(hex.EncodeToString(h.Sum(nil)))
}
