package packet

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"encoding/binary"
	"encoding/hex"
	"errors"
)

const AESKey = "F7A72DE7D6264530F01BA49BC73EB873"
const macKey = "B0FB83E39A5EBFAABE471362A58393FF"
const macIV = "D951DBE037C82325"

// FIXME: - 为减少内存操作，这里的[]byte应该使用*[]byte

func Encrypt(plaintext []byte, keyHex string, isPacket50 bool) ([]byte, error) {
	key := []byte(keyHex)

	// 构建密文结构体: 4 byte 明文长度+密文+8 byte MAC检验码
	result := []byte{}
	// 明文长度
	plaintextLen := len(plaintext)
	plaintextLenData := make([]byte, 4) // 如果写为 []byte{}，PutUint32时会报溢出
	binary.LittleEndian.PutUint32(plaintextLenData, uint32(plaintextLen))
	result = append(result, plaintextLenData...)
	// mac校验目标
	macPlainText := result[:]
	macPlainText = append(macPlainText, []byte{0, 0, 0, 0}...)

	// 对内容进行加密
	if isPacket50 {
		// 5.0 报文
		ciphertext, err := aesEncrypt(plaintext, key)
		// ciphertext, err := aesEncrypt(plaintext, key)
		if err != nil {
			return nil, err
		}
		result = append(result, ciphertext...)
	} else {
		// 4.0 报文

	}

	// MAC检验码
	// 注意：由于服务端的错误（遇到\0就停止），原本需要把长度+密文生成校验码，现改由长度4 byte进行校验即可
	macKeyData, _ := hex.DecodeString(macKey)
	macIVData, _ := hex.DecodeString(macIV)
	macData, err := macAnsi(macPlainText, macKeyData, macIVData)
	if err != nil {
		return nil, err
	}
	result = append(result, macData...)

	return result, nil
}

func Decrypt(ciphertext []byte, keyHex string, isPacket50 bool) ([]byte, error) {
	key := []byte(keyHex)

	// 对内容进行加密
	if isPacket50 {
		return aesDecrypt(ciphertext, key)
	} else {
		return nil, nil
	}
}

func aesEncrypt(plaintext, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, errors.New("错误的数据或密钥")
	}
	ecb := NewECBEncrypter(block)
	content := plaintext[:]
	content = PKCS5Padding(content, block.BlockSize())
	crypted := make([]byte, len(content))
	ecb.CryptBlocks(crypted, content)

	return crypted, nil
}

func aesDecrypt(crypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, errors.New("错误的数据或密钥")
	}
	blockMode := NewECBDecrypter(block)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)

	return origData, nil
}

// Des加密
func desEncrypt(plaintext []byte, key []byte) ([]byte, error) {
	if len(plaintext) < 1 || len(key) < 1 {
		return nil, errors.New("错误的数据或密钥")
	}
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	origData := PKCS5Padding(plaintext, blockSize)
	out := make([]byte, len(origData))
	dst := out

	// for len(origData) > 0 {
	// 	block.Encrypt(dst, origData[:blockSize])
	// 	origData = origData[blockSize:]
	// 	dst = dst[blockSize:]
	// }

	// return out, nil

	block.Encrypt(dst, origData[:blockSize])
	origData = origData[blockSize:]
	dst = dst[blockSize:]

	return out[0:8], nil
}

// Des解密
func desDecrypt(plaintext []byte, key []byte) ([]byte, error) {
	if len(plaintext) < 1 || len(key) < 1 {
		return nil, errors.New("wrong data or key")
	}
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	origData := PKCS5Padding(plaintext, blockSize)
	out := make([]byte, len(origData))
	dst := out

	// for len(origData) > 0 {
	// 	block.Decrypt(dst, origData[:blockSize])
	// 	origData = origData[blockSize:]
	// 	dst = dst[blockSize:]
	// }

	// return out, nil

	block.Decrypt(dst, origData[:blockSize])
	origData = origData[blockSize:]
	dst = dst[blockSize:]

	return out[0:8], nil
}

func macAnsiX99(plainText []byte, key []byte, iv []byte) ([]byte, error) {
	if len(plainText)%8 != 0 {
		return nil, errors.New("目标数据长度必须为8的整数倍")
	}
	if len(key) != 8 {
		return nil, errors.New("密钥位数不正确")
	}
	if len(iv) != 8 {
		return nil, errors.New("向量位数不正确")
	}

	macData := iv[:]
	xorData := make([]byte, 8)
	for i := 0; i < len(plainText); i += 8 {
		// 以8 byte为单位进行异或运算
		for j := 0; j < 8; j++ {
			xorData[j] = macData[j] ^ plainText[i+j]
		}

		// 异或结果的 8 byte 数据进行一次DES加密
		desData, err := desEncrypt(xorData, key)
		if err != nil {
			return nil, err
		}
		macData = desData[:]
	}

	return macData, nil
}

func macAnsiX919(plainText []byte, key []byte, iv []byte) ([]byte, error) {
	if len(plainText)%8 != 0 {
		return nil, errors.New("目标数据长度必须为8的整数倍")
	}
	if len(key) != 16 {
		return nil, errors.New("密钥位数不正确")
	}
	if len(iv) != 8 {
		return nil, errors.New("向量位数不正确")
	}

	// 分解密钥
	keyLeft := key[0:8]
	keyRight := key[8:16]

	// MAC(L)->R解密->L加密
	dataLeft, err := macAnsiX99(plainText, keyLeft, iv)
	if err != nil {
		return nil, err
	}
	dataRight, err := desDecrypt(dataLeft, keyRight)
	if err != nil {
		return nil, err
	}
	return desEncrypt(dataRight, keyLeft)
}

func macAnsi(plainText []byte, key []byte, iv []byte) ([]byte, error) {
	keyLen := len(key)
	if keyLen == 8 {
		return macAnsiX99(plainText, key, iv)
	} else if keyLen == 16 {
		return macAnsiX919(plainText, key, iv)
	} else {
		return nil, errors.New("密钥位数不正确")
	}
}

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

//ECB PKCS5Unpadding
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	if unpadding > unpadding {
		return []byte{}
	}
	return origData[:(length - unpadding)]
}

type ecb struct {
	b         cipher.Block
	blockSize int
}

func newECB(b cipher.Block) *ecb {
	return &ecb{
		b:         b,
		blockSize: b.BlockSize(),
	}
}

type ecbEncrypter ecb

// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
// mode, using the given Block.
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
	return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

type ecbDecrypter ecb

// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
// mode, using the given Block.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
	return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Decrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}
