// Package gateway 提供 Gateway API 加密和签名工具函数
package gateway

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"strconv"
	"strings"

	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

const aesBlockSize = 16

// 注意：AES-ECB 存在严重安全缺陷（相同明文块会产生相同密文块），
// 这是 Gateway 强制要求无法改变，生产环境必须使用 HTTPS 保障传输层安全。
func aesEncrypt(data []byte, appSecret string) (string, error) {
	key := md5.Sum([]byte(appSecret))
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return "", errors.Wrap(errors.CodeGatewayEncryptError, err, "数据加密失败")
	}

	// 使用 PKCS5 进行填充，确保明文长度为 16 的整数倍
	padded := pkcs5Padding(data, aesBlockSize)
	encrypted := make([]byte, len(padded))
	newECBEncrypter(block).CryptBlocks(encrypted, padded)
	return base64.StdEncoding.EncodeToString(encrypted), nil
}

// generateSign 生成 Gateway 签名（appId、data、timestamp、key 字母序）
func generateSign(appID, encryptedData string, timestamp int64, appSecret string) string {
	var builder strings.Builder
	builder.WriteString("appId=")
	builder.WriteString(appID)
	builder.WriteString("&data=")
	builder.WriteString(encryptedData)
	builder.WriteString("&timestamp=")
	builder.WriteString(strconv.FormatInt(timestamp, 10))
	builder.WriteString("&key=")
	builder.WriteString(appSecret)

	sum := md5.Sum([]byte(builder.String()))
	return strings.ToUpper(hex.EncodeToString(sum[:]))
}

// ecb 表示 AES-ECB 加密模式的基础结构
type ecb struct {
	b         cipher.Block
	blockSize int
}

type ecbEncrypter ecb

func newECBEncrypter(b cipher.Block) cipher.BlockMode {
	if b == nil {
		panic("crypto/cipher: 传入的加密块为空")
	}
	return &ecbEncrypter{b: b, blockSize: b.BlockSize()}
}

func (x *ecbEncrypter) BlockSize() int {
	return x.blockSize
}

func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: 输入数据不是完整块")
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

// pkcs5Padding 对明文进行 PKCS5 填充
func pkcs5Padding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padded := make([]byte, len(data)+padding)
	copy(padded, data)
	for i := len(data); i < len(padded); i++ {
		padded[i] = byte(padding)
	}
	return padded
}
