package containers

import (
	"debug/elf"
	"fmt"
	"github.com/coroot/coroot-node-agent/common"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

var libjvmRegex = regexp.MustCompile(`.*/libjvm\.so`)

func GetExeType(pid uint32) common.CodeType {
	mapsFilePath := fmt.Sprintf("/proc/%d/maps", pid)

	data, err := ioutil.ReadFile(mapsFilePath)
	if err != nil {
		log.Fatalf("Failed to read %s: %s", mapsFilePath, err)
	}

	content := string(data)

	if libjvmRegex.MatchString(content) {
		fmt.Println("is java process")
		if isJavaAotProcess(pid) {
			fmt.Println("is javaAot process")
			return common.CodeTypeJavaAot
		}
		return common.CodeTypeJava
	} else if isJavaAotProcess(pid) {
		fmt.Println("is javaAot process")
		return common.CodeTypeJavaAot
	} else if isGoProcess(pid) {
		fmt.Println("is go process")
		return common.CodeTypeGo
	} else if isNetCoreProcess(pid) {
		fmt.Println("is netcore process")
		return common.CodeTypeNetCoreAot
	}
	return common.CodeTypeUnknown
}

// isJavaAotProcess checks if the process with the given PID is a GraalVM native image
func isJavaAotProcess(pid uint32) bool {
	// Get the executable path for the given PID
	exePath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
	if err != nil {
		fmt.Printf("Error reading executable path for PID %d: %v\n", pid, err)
		return false
	}

	// Read the content of the executable file
	content, err := ioutil.ReadFile(exePath)
	if err != nil {
		fmt.Printf("Error reading executable file for PID %d: %v\n", pid, err)
		return false
	}

	// Check if the file contains the "graal_attach_thread" string
	if strings.Contains(string(content), "graal_attach_thread") {
		return true
	}

	return false
}

func isNetCoreProcess(pid uint32) bool {
	path, err := getProcessPath(pid)
	if err != nil {
		fmt.Printf("无法获取进程路径：%s\n", err)
		return false
	}
	ef, err := elf.Open(path)
	if err != nil {
		fmt.Println("failed to open as elf binary", err)
		return false
	}
	defer ef.Close()

	__managedcode := ef.Section("__managedcode")
	if __managedcode != nil {
		fmt.Println("is a netcore process")
		return true
	}
	return false
}

func isGoProcess(pid uint32) bool {
	path, err := getProcessPath(pid)
	if err != nil {
		fmt.Printf("无法获取进程路径：%s\n", err)
		return false
	}
	ef, err := elf.Open(path)
	if err != nil {
		fmt.Println("failed to open as elf binary", err)
		return false
	}
	defer ef.Close()

	gopclntabSection := ef.Section(".gopclntab")
	if gopclntabSection != nil {
		fmt.Println("is a go process")
		return true
	}
	return false
}

func getProcessPath(pid uint32) (string, error) {
	switch runtime.GOOS {
	case "linux":
		return getLinuxProcessPath(pid)
	default:
		return "", fmt.Errorf("不支持的操作系统：%s", runtime.GOOS)
	}
}

func getLinuxProcessPath(pid uint32) (string, error) {
	procPath := fmt.Sprintf("/proc/%d/exe", pid)
	path, err := os.Readlink(procPath)
	if err != nil {
		return "", err
	}
	return path, nil
}

func executeCommand(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	out, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return string(out), nil
}
