package common

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"crypto/md5"
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
)

// PasswordUtil 对应 Java 类的常量
const (
	// DefaultSalt 对应 Java 中的 SALT = "63293188"
	DefaultSalt = "63293188"
	// IterationCount 对应 Java 中的 ITERATIONCOUNT = 1000
	IterationCount = 1000
)

// GetSalt 生成 8 字节的随机盐
func GetSalt() ([]byte, error) {
	salt := make([]byte, 8)
	_, err := rand.Read(salt)
	if err != nil {
		return nil, err
	}
	return salt, nil
}

// GetStaticSalt 获取静态盐
func GetStaticSalt() []byte {
	return []byte(DefaultSalt)
}

// Encrypt 加密
// plaintext: 明文
// password: 密码
// salt: 盐 (传入 string, 对应 Java 的 salt 参数)
func Encrypt(plaintext, password, salt string) (string, error) {
	// 1. 生成 Key 和 IV
	key, iv := deriveKeyAndIV(password, salt, IterationCount)

	// 2. 创建 DES Cipher
	block, err := des.NewCipher(key)
	if err != nil {
		return "", err
	}

	// 3. 处理数据 (UTF-8 转 bytes 并填充)
	data := []byte(plaintext)
	data = pkcs5Padding(data, block.BlockSize())

	// 4. 加密 (CBC 模式)
	blockMode := cipher.NewCBCEncrypter(block, iv)
	crypted := make([]byte, len(data))
	blockMode.CryptBlocks(crypted, data)

	// 5. 转十六进制字符串 (对应 Java 的 bytesToHexString)
	// Java 的 Integer.toHexString 产生的是小写，这里保持一致，
	// 但通常十六进制可以互通。
	return hex.EncodeToString(crypted), nil
}

// Decrypt 解密
// ciphertext: 密文 (Hex 字符串)
// password: 密码
// salt: 盐
func Decrypt(ciphertext, password, salt string) (string, error) {
	// 1. 生成 Key 和 IV
	key, iv := deriveKeyAndIV(password, salt, IterationCount)

	// 2. Hex 字符串转 bytes
	decodedBytes, err := hex.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}

	// 3. 创建 DES Cipher
	block, err := des.NewCipher(key)
	if err != nil {
		return "", err
	}

	// 4. 解密 (CBC 模式)
	blockMode := cipher.NewCBCDecrypter(block, iv)
	// 密文长度检查
	if len(decodedBytes)%block.BlockSize() != 0 {
		return "", errors.New("ciphertext is not a multiple of the block size")
	}
	origData := make([]byte, len(decodedBytes))
	blockMode.CryptBlocks(origData, decodedBytes)

	// 5. 去除填充
	origData, err = pkcs5UnPadding(origData)
	if err != nil {
		return "", err
	}

	return string(origData), nil
}

// deriveKeyAndIV 模拟 Java PBEWithMD5AndDES 的密钥派生逻辑
// 逻辑：MD5(password || salt) -> 迭代 -> 结果前8字节是Key，后8字节是IV
func deriveKeyAndIV(password, salt string, iterations int) ([]byte, []byte) {
	// Java PBEKeySpec 处理 password 为 char[]，通常转 byte 时依赖编码，
	// 此处假设使用 UTF-8 兼容（Java 默认行为通常如此，或者 ASCII）
	passBytes := []byte(password)
	saltBytes := []byte(salt)

	// 第一次迭代: Hash(pass + salt)
	hash := md5.New()
	hash.Write(passBytes)
	hash.Write(saltBytes)
	derived := hash.Sum(nil)

	// 后续迭代: Hash(prev_hash)
	for i := 1; i < iterations; i++ {
		hash.Reset()
		hash.Write(derived)
		derived = hash.Sum(nil)
	}

	// MD5 结果是 16 字节
	// DES Key 需要 8 字节，IV 需要 8 字节
	// 正好平分
	key := derived[:8]
	iv := derived[8:]

	return key, iv
}

// pkcs5Padding 填充
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

// pkcs5UnPadding 去除填充
func pkcs5UnPadding(origData []byte) ([]byte, error) {
	length := len(origData)
	if length == 0 {
		return nil, errors.New("decryption error: output length is zero")
	}
	unpadding := int(origData[length-1])
	if length < unpadding {
		return nil, errors.New("decryption error: invalid padding")
	}
	return origData[:(length - unpadding)], nil
}

// --- 测试主函数 ---
func main() {
	// 测试数据
	plaintext := "admin"
	password := "Wang5322570"
	//salt := DefaultSalt
	salt := "RCGTeGiH"

	fmt.Println("原文:", plaintext)
	fmt.Println("密码:", password)
	fmt.Println("盐值:", salt)

	// 加密
	encrypted, err := Encrypt(plaintext, password, salt)
	if err != nil {
		fmt.Println("加密失败:", err)
		return
	}
	// Java代码可能输出小写或手动处理，这里使用 hex.EncodeToString (小写)
	// 如果需要大写可以使用 strings.ToUpper(encrypted)
	fmt.Println("加密密文 (Hex):", encrypted)

	// 解密
	decrypted, err := Decrypt(encrypted, password, salt)
	if err != nil {
		fmt.Println("解密失败:", err)
		return
	}
	fmt.Println("解密原文:", decrypted)

	// 验证一致性
	if plaintext == decrypted {
		fmt.Println(">> 测试通过！Go版本与逻辑一致。")
	} else {
		fmt.Println(">> 测试失败！")
	}
}
