package utils

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/net"
)

// 字符串生成md5
func CreatMD5(s string) string {
	data := []byte(s)
	has := md5.Sum(data)
	return fmt.Sprintf("%x", has)
}

func GetDiskId() string {
	partitions, err := disk.Partitions(false)
	if err != nil {
		return ""
	}
	return disk.GetDiskSerialNumber(partitions[0].Device)

}

// 获取物理网卡mac地址
func GetRealMacAddr() string {
	netInterfaces, err := net.Interfaces()
	if err != nil {
		return ""
	}
	loindex := 0
	for _, netInterface := range netInterfaces {
		if netInterface.Name == "lo" {
			loindex = netInterface.Index
			break
		}
	}
	if len(netInterfaces) < loindex+1 {
		return ""
	}
	fmt.Println(netInterfaces[loindex].HardwareAddr)
	return netInterfaces[loindex].HardwareAddr
}

func GetDeviceId() string {
	// deviceId := fmt.Sprintf("%s:%s", GetRealMacAddr(), GetDiskId())
	deviceId := GetRealMacAddr()
	fmt.Println(deviceId)
	return CreatMD5(deviceId)
}

// sha1()方法，返回的是byte转换的字符串，如果需要转成16进制，可以使用hex.EncodeToString
func GetSha1(str string) string {
	h := sha1.New()
	io.WriteString(h, str)
	return string(h.Sum(nil))
}

// 右边补全字符串实现方法，主要实现php的str_pad()方法
func StrPadRight(input string, padLength int, padString string) string {
	output := ""
	inputLen := len(input)
	if inputLen >= padLength {
		return input
	}
	ll := padLength - inputLen
	for i := 1; i <= ll; i = i + len(padString) {
		output += padString
	}
	return input + output
}

// 生成密钥
func GenSecretKey(appId string, appSecret string) (secretKey string) {
	key := appId + "&" + appSecret
	sha1_str := GetSha1(key)
	str10 := "0"
	pack64, _ := strconv.ParseUint(str10, 10, 32)
	fmt.Println(pack64)
	pack32 := uint32(pack64)
	str_pad := StrPadRight(sha1_str, 32, string(rune(pack32)))
	secretKey = base64.StdEncoding.EncodeToString([]byte(str_pad))
	return secretKey
}

// 解密
func GetCallbackData(secretKey string, encryptData string) (result_str string, err error) {
	decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey)
	decodeSecretKey := string(decodeSecretKeyByte)
	orderSuccessPayInfoByte, err2 := DecodeAesGcm(encryptData, decodeSecretKey, "")
	result_str = string(orderSuccessPayInfoByte)
	if err2 != nil {
		return result_str, errors.New("decode error")
	}
	return result_str, err
}

// 加密
func SetCallbackData(secretKey string, data string) (result_str string, err error) {
	decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey)
	decodeSecretKey := string(decodeSecretKeyByte)
	orderSuccessPayInfoByte, err2 := EncodeAesGcm(data, decodeSecretKey, "")
	result_str = string(orderSuccessPayInfoByte)
	if err2 != nil {
		return result_str, errors.New("encode error")
	}
	return result_str, err
}

// url安全模式decode字符串
func UrlSafeB64decode(str string) (result []byte) {
	str = strings.Replace(str, "-", "+", -1)
	str = strings.Replace(str, "_", "/", -1)
	mod4 := len(str) % 4
	if mod4 != 0 {
		str = str + "===="[0:mod4]
	}
	result, _ = base64.StdEncoding.DecodeString(str)
	return result
}

// base64字符串，替换转换为安全模式
func UrlSafeB64encode(str string) (result string) {
	str = strings.Replace(str, "+", "-", -1)
	str = strings.Replace(str, "/", "_", -1)
	return str
}

// 使用aes-256-gcm方式解密字符串
func DecodeAesGcm(encryptData string, hex_key string, hex_add string) ([]byte, error) {
	tagSize := 16 //nonceSize,tag的长度，用于open时候生成tag,默认12
	key := []byte(hex_key)
	add := []byte(hex_add)
	block, err := aes.NewCipher(key) //生成加解密用的block
	if err != nil {
		return []byte(""), err
	}
	//根据不同加密算法，也有不同tag长度的方法设定和调用，比如NewGCMWithTagSize、newGCMWithNonceAndTagSize
	aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize)
	if err != nil {
		return []byte(""), err
	}
	decodeEncryptStr := UrlSafeB64decode(encryptData)
	ciphertext := decodeEncryptStr
	if len(ciphertext) <= aesgcm.NonceSize() { // 长度应该>iv
		return []byte(""), errors.New("string: too short") //解密失败
	}
	iv := ciphertext[:aesgcm.NonceSize()]        //分离出IV
	ciphertext = ciphertext[aesgcm.NonceSize():] // 密文,tag是调用open方法时候通过密文和前面new时候传的size来进行截取的
	plaintext, err := aesgcm.Open(nil, iv, ciphertext, add)
	return plaintext, err
}

// 使用aes-256-gcm加密数据
func EncodeAesGcm(data string, hex_key string, hex_add string) (result string, error error) {
	tagSize := 16 //nonceSize,tag的长度，用于open时候生成tag,默认12
	key := []byte(hex_key)
	add := []byte(hex_add)
	block, err := aes.NewCipher(key) //生成加解密用的block
	if err != nil {
		return result, err
	}
	//根据不同加密算法，也有不同tag长度的方法设定和调用，比如NewGCMWithTagSize、newGCMWithNonceAndTagSize
	aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize)
	if err != nil {
		return result, err
	}
	plaintext := []byte(data)
	iv := make([]byte, tagSize)                       // NonceSize=12
	rand.Read(iv)                                     //获取随机值
	ciphertext := aesgcm.Seal(iv, iv, plaintext, add) //加密,密文为:iv+密文+tag
	result = base64.StdEncoding.EncodeToString(ciphertext)
	result = UrlSafeB64encode(result)
	return result, nil // 生成的BS64
}
