package mycrypto

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
)

var (
	defaultAes256Cbc *Aes256Cbc
)

type Aes256Cbc struct {
	Iv  []byte // iv向量 (key 32字节 256位)
	Key []byte // 加密密钥 (iv 16字节 128位)
}

func NewAes256Cbc(key, iv string) *Aes256Cbc {
	if len(key) != 32 {
		panic("密钥key长度应为32")
	}

	if len(iv) != aes.BlockSize {
		panic("iv长度应为16")
	}
	return &Aes256Cbc{
		Iv:  []byte(iv),
		Key: []byte(key),
	}
}
func InitAes256Cbc(key, iv string) {
	defaultAes256Cbc = NewAes256Cbc(key, iv)
}

func Aes256CbcEncrypt(plainText string) (string, error) {
	return defaultAes256Cbc.Encrypt(plainText)
}

func Aes256CbcDecrypt(cipherText string) (string, error) {
	return defaultAes256Cbc.Decrypt(cipherText)
}

// Encrypt 加密
func (a *Aes256Cbc) Encrypt(plainText string) (res string, err error) {
	defer func() {
		if err2 := recover(); err2 != nil {
			err = fmt.Errorf("panic: %s", err2)
		}
	}()

	// PKCS5填充，补齐数据块
	bPlaintext := a.PKCS5Padding([]byte(plainText), aes.BlockSize)
	block, err := aes.NewCipher(a.Key)
	if err != nil {
		return plainText, err
	}

	ciphertext := make([]byte, len(bPlaintext))
	mode := cipher.NewCBCEncrypter(block, a.Iv)
	mode.CryptBlocks(ciphertext, bPlaintext)

	return hex.EncodeToString(ciphertext), nil
}

// Decrypt 解密
func (a *Aes256Cbc) Decrypt(cipherText string) (res string, err error) {
	defer func() {
		if err2 := recover(); err2 != nil {
			err = fmt.Errorf("panic: %s", err2)
		}
	}()

	block, err := aes.NewCipher(a.Key)
	if err != nil {
		return "", err
	}

	decodeString, err := hex.DecodeString(cipherText)
	if err != nil {
		return "", err
	}

	mode := cipher.NewCBCDecrypter(block, a.Iv)
	mode.CryptBlocks(decodeString, decodeString)

	// 移除多余填充的补充位
	return string(a.PKCS5UnPadding(decodeString)), nil
}

func (a *Aes256Cbc) PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func (a *Aes256Cbc) PKCS5UnPadding(src []byte) []byte {
	length := len(src)
	unpadding := int(src[length-1])
	return src[:(length - unpadding)]
}
