package public

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
)

const (
	DMIDECODE = "dmidecode"
)

func CheckCmdExists(command string) (string, error) {
	path, err := exec.LookPath(command)
	if err != nil {
		if runtime.GOOS != "darwin" || //macos,windows测试使用,不获取硬盘分区UUID
			runtime.GOOS != "windows" {
			fmt.Printf("didn't find 'blkid' executable,err %s\n", err.Error())
		}
		return "", err
	}
	return path, nil
}

func IsExistFile(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

func ReadSysFile(filePath string) ([]byte, error) {
	isExist := IsExistFile(filePath)
	if isExist == false {
		return nil, fmt.Errorf("%s file does not exist", filePath)
	}

	fd, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
	if err != nil {
		if os.IsPermission(err) {
			return nil, fmt.Errorf("%s", "permission denied get machine id")
		}
		return nil, fmt.Errorf("%s", "get machine id failed")
	}
	defer fd.Close()

	fstabText, err := ioutil.ReadAll(fd)
	if err != nil {
		return nil, err
	}

	return fstabText, nil
}

//降低数字串长度
func Sum(data []byte) string {
	var (
		sum    uint64
		length int = len(data)
		index  int
	)

	//以32位求和
	for length >= 4 {
		sum += uint64(data[index])<<24 + uint64(data[index+1])<<16 + uint64(data[index+2])<<8 + uint64(data[index+3])
		index += 4
		length -= 4
	}

	switch length {
	case 3:
		sum += uint64(data[index])<<16 + uint64(data[index+1])<<8 + uint64(data[index+2])
	case 2:
		sum += uint64(data[index])<<8 + uint64(data[index+1])
	case 1:
		sum += uint64(data[index])
	case 0:
		break
	}

	return strconv.FormatUint(sum, 16)
}

//获取BIOS出厂UUID
func GetProductUUID() (string, error) {
	if runtime.GOOS == "darwin" {
		return GetProductUUIDMac()
	}

	if runtime.GOOS == "windows" {
		return GetProductUUIDWin()
	}

	cmd := exec.Command("dmidecode", "-s", "system-uuid")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}

	if len(output) == 0 {
		return "", fmt.Errorf("%s", "uuid is empty string")
	}

	system_uuid := string(output)

	cmd = exec.Command("dmidecode", "-s", "system-serial-number")
	output, err = cmd.CombinedOutput()
	if err != nil {
		return "", err
	}

	if len(output) == 0 {
		return "", fmt.Errorf("%s", "serial-number is empty string")
	}

	system_serial_number := string(output)

	deviceID := system_uuid + "_starwiz_" + system_serial_number
	return GetMd5String(deviceID), nil
}

/*
GetMachineID 获取机器ID (分区UUID和网卡地址 or Product UUID)
*/
func GetMachineID() (string, error) {
	var (
		MachineIDList []string
	)

	productUUID, err := GetProductUUID()
	if err != nil {
		return "", err
	}
	MachineIDList = append(MachineIDList, productUUID)

	//编码
	encByte, err := json.Marshal(MachineIDList)
	if err != nil {
		return "", err
	}

	//降低长度
	return Sum(encByte), nil
}

func GetMd5String(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

func GetProductUUIDMac() (string, error) {
	// 从系统调用获取硬件 UUID
	out, err := exec.Command("system_profiler", "SPHardwareDataType").Output()
	if err != nil {
		return "", err
	}

	// 解析输出以获取 UUID
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		if strings.Contains(line, "Hardware UUID:") {
			parts := strings.Split(line, ":")
			if len(parts) == 2 {
				return strings.TrimSpace(parts[1]), nil
			}
		}
	}

	return "", fmt.Errorf("could not find hardware UUID in system_profiler output")
}

func GetProductUUIDWin() (string, error) {
	// 使用系统调用获取计算机的注册表信息
	out, err := exec.Command("reg", "query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid").Output()
	if err != nil {
		return "", err
	}

	// 解析注册表输出以获取 MachineGuid
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		if strings.Contains(line, "REG_SZ") {
			parts := strings.Fields(line)
			if len(parts) >= 3 {
				return strings.TrimSpace(parts[2]), nil
			}
		}
	}

	return "", fmt.Errorf("MachineGuid not found in registry")
}
