package service

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"main/constant"
	"main/mt"
	"sort"
	"strings"
)

func (wp *wxpay) GenSHA256Signature(str string, key string) string {
	mac := hmac.New(sha256.New, []byte(key))
	_, _ = mac.Write([]byte(str))
	return strings.ToLower(hex.EncodeToString(mac.Sum(nil)))
}

func (wp *wxpay) GetPurchaseSig(gameid int64, data string, sessionkey string) (pay_sig string, signature string) {
	cfg := mt.Table.Wxconfig.GetById(gameid)
	if cfg == nil {
		return
	}
	pay_sig = wp.GenSHA256Signature("requestMidasPaymentGameItem&"+data, cfg.GetAppkey())
	signature = wp.GenSHA256Signature(data, sessionkey)

	return pay_sig, signature

}

func (wp *wxpay) DecryptMsg(sMsgSignature string, sTimeStamp string, sNonce string, sEncryptMsg string, token string, aeskey string) (sMsg []byte, msgappid []byte) {

	// 2.validate signature
	if !wp.ValidateSignature(sMsgSignature, sTimeStamp, sNonce, sEncryptMsg, token) {
		return
	}

	//3.decode base64
	sAesData, err := base64.StdEncoding.DecodeString(sEncryptMsg)
	if err != nil {
		return
	}

	//4.decode aes
	sAesKey := wp.GenAesKeyFromEncodingKey(aeskey)
	if len(sAesKey) == 0 {
		return
	}
	sNoEncryptData := wp.AES_CBCDecrypt(sAesData, sAesKey)

	// 5. remove kRandEncryptStrLen str
	if len(sNoEncryptData) <= constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN {
		return
	}
	netlenbyte := sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN : constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN]
	buf := bytes.NewReader(netlenbyte)
	iMsgLen := int32(0) //ntohl(iNetLen);
	binary.Read(buf, binary.BigEndian, &iMsgLen)
	if int(iMsgLen) < 1 || len(sNoEncryptData) <= constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+int(iMsgLen) {
		return
	}
	sMsg = sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN : constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+iMsgLen]

	//6. validate appid
	msgappid = sNoEncryptData[constant.WX_RANDENCRYPT_STRLEN+constant.WX_KMSG_LEN+iMsgLen:]
	return
}

func (wp *wxpay) ValidateSignature(sMsgSignature string, sTimeStamp string, sNonce string, sEncryptMsg string, token string) bool {
	sSignature := wp.ComputeSignature(token, sTimeStamp, sNonce, sEncryptMsg)
	if sSignature == "" {
		return false
	}

	return sMsgSignature == sSignature
}

func (wp *wxpay) ComputeSignature(sToken string, sTimeStamp string, sNonce string, sMessage string) string {
	if sToken == "" || sNonce == "" || sMessage == "" || sTimeStamp == "" {
		return ""
	}

	//sort
	strs := []string{}
	strs = append(strs, sToken)
	strs = append(strs, sTimeStamp)
	strs = append(strs, sNonce)
	strs = append(strs, sMessage)
	sort.Strings(strs)
	sStr := strs[0] + strs[1] + strs[2] + strs[3]

	//compute
	sha1crypto := sha1.New()
	_, err := sha1crypto.Write([]byte(sStr))
	if err != nil {
		return ""
	}

	return hex.EncodeToString(sha1crypto.Sum(nil))
}

func (wp *wxpay) GenAesKeyFromEncodingKey(sEncodingKey string) (aeskey []byte) {
	if len(sEncodingKey) != constant.WX_ENCODING_KEY_SIZE {
		return
	}

	sBase64 := sEncodingKey + "="
	data, err := base64.StdEncoding.DecodeString(sBase64)
	if err != nil {
		return
	}

	return data
}

func (wp *wxpay) AES_CBCDecrypt(sSource []byte, sKey []byte) []byte {
	if len(sSource) < constant.WX_AESKEY_SIZE || len(sSource)%constant.WX_AESKEY_SIZE != 0 {
		return []byte{}
	}

	key := sKey
	if len(sKey) > constant.WX_AESKEY_SIZE {
		key = key[0:constant.WX_AESKEY_SIZE]
	}
	return aesDecryptCBC(sSource, key)
}

func aesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)                              // 分组秘钥
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
	decrypted = make([]byte, len(encrypted))                    // 创建数组
	blockMode.CryptBlocks(decrypted, encrypted)                 // 解密
	decrypted = pkcs5UnPadding(decrypted)                       // 去除补全码
	return decrypted
}

func pkcs5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
