package verify

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"

	"github.com/gogf/gf/v2/errors/gerror"
)

// AesCBCEncrypt AES/CBC/PKCS7Padding 加密
func aesCBCEncrypt(plaintext []byte, key []byte, iv []byte) ([]byte, error) {
	// AES
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// PKCS7 填充
	plaintext = paddingPKCS7(plaintext, aes.BlockSize)

	// CBC 加密
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(plaintext, plaintext)

	return plaintext, nil
}

func AesCBCEncryptWithBase64(ciphertext string, key []byte, iv []byte) (string, error) {
	result, err := aesCBCEncrypt([]byte(ciphertext), key, iv)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(result), nil
}

// aesCBCDecrypt AES/CBC/PKCS7Padding 解密
func aesCBCDecrypt(ciphertext []byte, key []byte, iv []byte) ([]byte, error) {
	//吸收 panic
	defer func() {
		if err := recover(); err != nil {
			return
		}
	}()
	// AES
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	if len(ciphertext)%aes.BlockSize != 0 {
		return []byte(""), gerror.New("解码失败")
	}

	// CBC 解密
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(ciphertext, ciphertext)

	// PKCS7 反填充
	result := unPaddingPKCS7(ciphertext)
	return result, nil
}

func AesCBCStdDecryptWithBase64(ciphertext string, key []byte, iv []byte) ([]byte, error) {
	text, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return text, err
	}
	return aesCBCDecrypt(text, key, iv)
}

func AesCBCURLDecryptWithBase64(ciphertext string, key []byte, iv []byte) ([]byte, error) {
	text, err := base64.URLEncoding.DecodeString(ciphertext)
	if err != nil {
		return text, err
	}
	return aesCBCDecrypt(text, key, iv)
}

// PKCS7 填充
func paddingPKCS7(plaintext []byte, blockSize int) []byte {
	paddingSize := blockSize - len(plaintext)%blockSize
	paddingText := bytes.Repeat([]byte{byte(paddingSize)}, paddingSize)
	return append(plaintext, paddingText...)
}

// PKCS7 反填充
func unPaddingPKCS7(s []byte) []byte {
	length := len(s)
	if length == 0 {
		return s
	}
	unPadding := int(s[length-1])
	return s[:(length - unPadding)]
}
