package utils

import (
	"applet/app/cfg"
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"fmt"
)

func AesEncrypt(rawData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	rawData = PKCS5Padding(rawData, blockSize)
	// rawData = ZeroPadding(rawData, block.BlockSize())
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
	encrypted := make([]byte, len(rawData))
	// 根据CryptBlocks方法的说明，如下方式初始化encrypted也可以
	// encrypted := rawData
	blockMode.CryptBlocks(encrypted, rawData)
	return encrypted, nil
}

func AesDecrypt(encrypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
	rawData := make([]byte, len(encrypted))
	// rawData := encrypted
	blockMode.CryptBlocks(rawData, encrypted)
	rawData = PKCS5UnPadding(rawData)
	// rawData = ZeroUnPadding(rawData)
	return rawData, nil
}

func ZeroPadding(cipherText []byte, blockSize int) []byte {
	padding := blockSize - len(cipherText)%blockSize
	padText := bytes.Repeat([]byte{0}, padding)
	return append(cipherText, padText...)
}

func ZeroUnPadding(rawData []byte) []byte {
	length := len(rawData)
	unPadding := int(rawData[length-1])
	return rawData[:(length - unPadding)]
}

func PKCS5Padding(cipherText []byte, blockSize int) []byte {
	padding := blockSize - len(cipherText)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(cipherText, padText...)
}

func PKCS5UnPadding(rawData []byte) []byte {
	length := len(rawData)
	// 去掉最后一个字节 unPadding 次
	unPadding := int(rawData[length-1])
	return rawData[:(length - unPadding)]
}

// 填充0
func zeroFill(key *string) {
	l := len(*key)
	if l != 16 && l != 24 && l != 32 {
		if l < 16 {
			*key = *key + fmt.Sprintf("%0*d", 16-l, 0)
		} else if l < 24 {
			*key = *key + fmt.Sprintf("%0*d", 24-l, 0)
		} else if l < 32 {
			*key = *key + fmt.Sprintf("%0*d", 32-l, 0)
		} else {
			*key = string([]byte(*key)[:32])
		}
	}
}

type AesCrypt struct {
	Key []byte
	Iv  []byte
}

func (a *AesCrypt) Encrypt(data []byte) ([]byte, error) {
	aesBlockEncrypt, err := aes.NewCipher(a.Key)
	if err != nil {
		println(err.Error())
		return nil, err
	}

	content := pKCS5Padding(data, aesBlockEncrypt.BlockSize())
	cipherBytes := make([]byte, len(content))
	aesEncrypt := cipher.NewCBCEncrypter(aesBlockEncrypt, a.Iv)
	aesEncrypt.CryptBlocks(cipherBytes, content)
	return cipherBytes, nil
}

func (a *AesCrypt) Decrypt(src []byte) (data []byte, err error) {
	decrypted := make([]byte, len(src))
	var aesBlockDecrypt cipher.Block
	aesBlockDecrypt, err = aes.NewCipher(a.Key)
	if err != nil {
		println(err.Error())
		return nil, err
	}
	aesDecrypt := cipher.NewCBCDecrypter(aesBlockDecrypt, a.Iv)
	aesDecrypt.CryptBlocks(decrypted, src)
	return pKCS5Trimming(decrypted), nil
}

func pKCS5Padding(cipherText []byte, blockSize int) []byte {
	padding := blockSize - len(cipherText)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(cipherText, padText...)
}

func pKCS5Trimming(encrypt []byte) []byte {
	padding := encrypt[len(encrypt)-1]
	return encrypt[:len(encrypt)-int(padding)]
}

// AesAdminCurlPOST is 与后台接口加密交互
func AesAdminCurlPOST(aesData string, url string, uid int) ([]byte, error) {
	adminKey := cfg.Admin.AesKey
	adminVI := cfg.Admin.AesIV
	crypto := AesCrypt{
		Key: []byte(adminKey),
		Iv:  []byte(adminVI),
	}

	encrypt, err := crypto.Encrypt([]byte(aesData))
	if err != nil {
		return nil, err
	}

	// 发送请求到后台
	postData := map[string]string{
		"postData": base64.StdEncoding.EncodeToString(encrypt),
	}
	fmt.Println(adminKey)
	fmt.Println(adminVI)
	fmt.Println("=======ADMIN请求=====")
	fmt.Println(postData)
	postDataByte, _ := json.Marshal(postData)
	rdata, err := CurlPost(url, postDataByte, nil)
	fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>", err)

	if err != nil {
		return nil, err
	}

	FilePutContents("cash_out", fmt.Sprintf("curl Result返回：%s uid：%d, >>>>>>>>>>>>>>>>>>>>", string(rdata), uid))
	pass, err := base64.StdEncoding.DecodeString(string(rdata))
	if err != nil {
		return nil, err
	}
	fmt.Println(pass)

	decrypt, err := crypto.Decrypt(pass)
	fmt.Println(err)

	if err != nil {
		return nil, err
	}
	return decrypt, nil
}
