//go:build linux

// sandbox_linux.go v1.5

package sandbox

import (
	_ "embed"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"apigo.cc/gojs"
	"github.com/ssgo/log"
	"github.com/ssgo/u"
)

//go:embed init.gz
var initGz []byte
var initExec []byte

func init() {
	initExec = u.GunzipN(initGz)
}

var defaultVolumes = []Volume{
	{Source: "/usr", Target: "/usr", ReadOnly: true},
	{Source: "/lib", Target: "/lib", ReadOnly: true},
	{Source: "/lib64", Target: "/lib64", ReadOnly: true},
	{Source: "/bin", Target: "/bin", ReadOnly: true},
	{Source: "/sbin", Target: "/sbin", ReadOnly: true},
	{Source: "/etc/resolv.conf", Target: "/etc/resolv.conf", ReadOnly: true},
	{Source: "/etc/hosts", Target: "/etc/hosts", ReadOnly: true},
	{Source: "/etc/localtime", Target: "/etc/localtime", ReadOnly: true},
	{Source: "/dev/null", Target: "/dev/null", ReadOnly: false},
	{Source: "/dev/urandom", Target: "/dev/urandom", ReadOnly: true},
	{Source: "/dev/zero", Target: "/dev/zero", ReadOnly: true},
}

func Start(cfg *Config) (*Sandbox, error) {
	s := &Sandbox{
		config:      cfg,
		mountedList: []string{},
		status:      "created",
		extra:       map[string]any{},
	}
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.status != "created" {
		return nil, gojs.Err("sandbox is already used")
	}

	if s.config.WorkDir == "" {
		s.config.WorkDir = "/app"
	}

	if os.Getuid() == 0 {
		if user, err := user.Lookup("nobody"); err == nil {
			s.uid = u.Int(user.Uid)
			s.gid = u.Int(user.Gid)
		} else {
			s.uid = 65534
			s.gid = 65534
		}
	}

	initEnv := map[string]string{
		"LANG":            os.Getenv("LANG"),
		"LC_TIME":         os.Getenv("LC_TIME"),
		"PATH":            filepath.Join(s.config.WorkDir, "bin") + ":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
		"HOME":            s.config.WorkDir,
		"LD_LIBRARY_PATH": os.Getenv("LD_LIBRARY_PATH"),
	}
	if initEnv["LC_TIME"] == "" {
		initEnv["LC_TIME"] = "C"
	}
	if initEnv["LANG"] == "" {
		initEnv["LANG"] = "C.UTF-8"
	}
	addEnv := func(k, v string) {
		initEnv[k] = os.Expand(v, func(varName string) string {
			return initEnv[varName]
		})
	}

	s.status = "starting"
	s.startTime = time.Now().Unix()
	s.id = getId()
	s.root = filepath.Join(pluginConfig.Root, s.id)

	// 1. 构建物理环境 (对应原 mountAll 逻辑)
	initPaths := []string{s.config.WorkDir, "/var/log", "/etc", "/proc", "/sys"}
	os.MkdirAll(s.root, 0755)
	if s.uid != 0 {
		os.Chown(s.root, s.uid, s.gid)
	}
	for _, p := range initPaths {
		path := filepath.Join(s.root, p)
		os.MkdirAll(path, 0755)
		if s.uid != 0 {
			os.Chown(path, s.uid, s.gid)
		}
	}

	// 2. 执行挂载逻辑 (严格保留 v20 的 bind mount + remount ro 逻辑)
	vs := NewVolumes()
	vs.Add(defaultVolumes...)
	vs.Add(s.config.Volumes...)
	if s.config.Limits.Shm > 0 {
		vs.Add(Volume{Source: "tmpfs", Target: "/dev/shm"})
	}
	if s.config.Limits.Tmp > 0 {
		vs.Add(Volume{Source: "tmpfs", Target: "/tmp"})
	}
	// 处理 Gpu 透传
	if len(s.config.Gpu.Devices) > 0 {
		gpuDevices := []string{}
		driver := strings.ToLower(s.config.Gpu.Driver)

		switch driver {
		case "nvidia":
			// 核心管理与工具设备
			gpuDevices = append(gpuDevices, "/dev/nvidiactl", "/dev/nvidia-uvm", "/dev/nvidia-uvm-tools", "/dev/nvidia-modeset")
			// 具体显卡设备
			if len(s.config.Gpu.Devices) > 0 && s.config.Gpu.Devices[0] == "all" {
				matches, _ := filepath.Glob("/dev/nvidia[0-9]*")
				gpuDevices = append(gpuDevices, matches...)
			} else {
				for _, devId := range s.config.Gpu.Devices {
					gpuDevices = append(gpuDevices, "/dev/nvidia"+devId)
				}
			}

			// 设置环境变量
			addEnv("CUDA_HOME", os.Getenv("CUDA_HOME"))
			addEnv("NVIDIA_DRIVER_CAPABILITIES", strings.Join(s.config.Gpu.Devices, ","))

		case "amd", "render":
			// AMD GPU 或通用的 DRM 渲染设备 (Intel 也可以走此逻辑)
			// DRI (Direct Rendering Infrastructure) 设备
			gpuDevices = append(gpuDevices, "/dev/kfd") // AMD 核心调度器
			if len(s.config.Gpu.Devices) > 0 && s.config.Gpu.Devices[0] == "all" {
				matches, _ := filepath.Glob("/dev/dri/renderD*")
				gpuDevices = append(gpuDevices, matches...)
				matchesCard, _ := filepath.Glob("/dev/dri/card*")
				gpuDevices = append(gpuDevices, matchesCard...)
			} else {
				for _, devId := range s.config.Gpu.Devices {
					// 常见的 ID 如 128 (renderD128)
					gpuDevices = append(gpuDevices, "/dev/dri/renderD"+devId)
				}
			}
			addEnv("ROCR_VISIBLE_DEVICES", strings.Join(s.config.Gpu.Devices, ","))
			// 对于某些旧版 OpenCL
			addEnv("GPU_DEVICE_ORDINAL", strings.Join(s.config.Gpu.Devices, ","))

		case "intel":
			// Intel 特有的设备节点通常就在 /dev/dri 目录下
			if len(s.config.Gpu.Devices) > 0 && s.config.Gpu.Devices[0] == "all" {
				matches, _ := filepath.Glob("/dev/dri/*")
				gpuDevices = append(gpuDevices, matches...)
			} else {
				for _, devId := range s.config.Gpu.Devices {
					gpuDevices = append(gpuDevices, "/dev/dri/renderD"+devId)
				}
			}
			addEnv("ONEAPI_DEVICE_SELECTOR", "level_zero:"+strings.Join(s.config.Gpu.Devices, ","))
		}

		// 统一执行设备挂载
		for _, dev := range gpuDevices {
			if u.FileExists(dev) {
				vs.Add(Volume{Source: dev, Target: dev})
			}
		}
	}

	// 处理 ProjectDir 自动挂载
	if s.config.ProjectDir != "" {
		s.workDir = u.GetAbsFilename(s.config.ProjectDir)
	} else {
		// 使用临时目录
		s.workDir = filepath.Join(s.root, ".workdir")
	}

	if !u.FileExists(s.workDir) {
		os.MkdirAll(s.workDir, 0755)
		if s.uid != 0 {
			os.Chown(s.workDir, s.uid, s.gid)
		}
	}
	vs.Add(Volume{Source: s.workDir, Target: s.config.WorkDir})

	// 处理 runtime
	runtimeListLock.RLock()
	rt := runtimeList[s.config.Runtime.Language]
	runtimeListLock.RUnlock()
	if rt != nil {
		rtCfg := pluginConfig.Runtime[s.config.Runtime.Language]
		if rtCfg == nil {
			rtCfg = &RuntimeConfig{}
		}
		if rtCfg.Root == "" {
			rtCfg.Root = filepath.Join(pluginConfig.Root, s.config.Runtime.Language)
		}
		runtimePath := filepath.Join(rtCfg.Root, "runtime", s.config.Runtime.Version)
		venvPath := filepath.Join(rtCfg.Root, "venv", fmt.Sprintf("%s_%s", s.config.Runtime.Version, s.config.Runtime.Venv))
		if rtsbCfg, err := rt.Check(runtimePath, venvPath, s.config.ProjectDir, s.uid, s.gid, rtCfg); err == nil {
			if s.config.StartCmd == "" && rtsbCfg.StartCmd != "" {
				s.config.StartCmd = rtsbCfg.StartCmd
			}
			for k, v := range rtsbCfg.Envs {
				addEnv(k, v)
			}
			// 挂载运行时环境
			if u.FileExists(runtimePath) {
				vs.Add(Volume{Source: runtimePath, Target: runtimePath, ReadOnly: true})
			}
			if u.FileExists(venvPath) {
				vs.Add(Volume{Source: venvPath, Target: venvPath, ReadOnly: false})
			}
		} else {
			s._cleanup()
			return nil, err
		}
	} else {
		s._cleanup()
		return nil, gojs.Err("runtime not found for language " + s.config.Runtime.Language)
	}

	if s.config.StartCmd == "" {
		s._cleanup()
		return nil, gojs.Err("start cmd is empty")
	}

	for k, v := range s.config.Envs {
		addEnv(k, v)
	}

	for _, v := range vs.Get() {
		from, to, mode := v.Source, v.Target, "rw"
		if v.ReadOnly {
			mode = "ro"
		}

		toPath := filepath.Join(s.root, to)
		fi, err := os.Lstat(from)

		if err == nil && fi.Mode()&os.ModeSymlink != 0 {
			// 处理符号链接
			linkTarget, _ := os.Readlink(from)
			os.Symlink(linkTarget, toPath)
		} else {
			// 创建挂载点
			if err != nil || fi.IsDir() {
				// 创建文件夹（如果err!=nil表示这是特殊文件夹例如/proc所以也要创建文件夹）
				os.MkdirAll(toPath, 0755)
				if s.uid != 0 {
					os.Chown(toPath, s.uid, s.gid)
				}
			} else {
				// 写入空文件（u.WriteFile会自动创建文件夹）
				u.WriteFile(toPath, "")
				if s.uid != 0 {
					os.Chown(toPath, s.uid, s.gid)
				}
			}

			// 挂载操作
			var flags uintptr = syscall.MS_BIND | syscall.MS_REC
			fsType, opt := "", ""
			if from == "tmpfs" {
				fsType = "tmpfs"
				flags = 0
				// 根据配置处理 shm/tmp 大小
				if to == "/dev/shm" && s.config.Limits.Shm > 0 {
					opt = "size=" + strconv.FormatUint(uint64(s.config.Limits.Shm), 10) + "m"
				} else if to == "/tmp" && s.config.Limits.Tmp > 0 {
					opt = "size=" + strconv.FormatUint(uint64(s.config.Limits.Tmp), 10) + "m"
				}
			}

			err = syscall.Mount(from, toPath, fsType, flags, opt)
			if mode == "ro" && err == nil && fsType == "" {
				// bind模式下ReadOnly需要二次挂载才能实现
				syscall.Mount("", toPath, "", flags|syscall.MS_REMOUNT|syscall.MS_RDONLY, "")
			} else if s.uid != 0 && mode != "ro" && fsType == "" && !strings.HasPrefix(to, "/dev") {
				// 可写的普通挂载，设置为用户权限
				os.Lchown(toPath, s.uid, s.gid)
			}
			s.assertLog(fmt.Sprintf("Mount %s to %s", from, toPath), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir, "readonly", v.ReadOnly)
			if err == nil {
				s.mountedList = append(s.mountedList, toPath)
			}
		}
	}
	// 容器内会自动挂载/proc和/sys，需要在结束时自动卸载
	s.mountedList = append(s.mountedList, filepath.Join(s.root, "/proc"), filepath.Join(s.root, "/sys"))

	// 3. Cgroup v2 资源限制
	cgPath := "/sys/fs/cgroup/" + s.id
	enableCgroup := false
	if s.config.Limits.Mem >= 0.0001 {
		err := u.WriteFile(filepath.Join(cgPath, "memory.max"), u.String(int64(s.config.Limits.Mem*1024*1024*1024)))
		s.assertLog(fmt.Sprintf("Write %s to %s", u.String(s.config.Limits.Mem*1024*1024*1024), filepath.Join(cgPath, "memory.max")), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
		if err == nil {
			enableCgroup = true
		}
	}
	if s.config.Limits.Swap >= 0.0001 || s.config.Limits.Mem >= 0.0001 {
		err := u.WriteFile(filepath.Join(cgPath, "memory.swap.max"), u.String(int64(s.config.Limits.Swap*1024*1024*1024)))
		s.assertLog(fmt.Sprintf("Write %s to %s", u.String(s.config.Limits.Swap*1024*1024*1024), filepath.Join(cgPath, "memory.swap.max")), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
		if err == nil {
			enableCgroup = true
		}
	}

	if s.config.Limits.Cpu >= 0.0001 {
		err := u.WriteFile(filepath.Join(cgPath, "cpu.max"), fmt.Sprintf("%d 100000", int(s.config.Limits.Cpu*100000)))
		s.assertLog(fmt.Sprintf("Write %s to %s", fmt.Sprintf("%d 100000", int(s.config.Limits.Cpu*100000)), filepath.Join(cgPath, "cpu.max")), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
		if err == nil {
			enableCgroup = true
		}
	}
	if (s.config.Limits.Mem >= 0.0001 || s.config.Limits.Swap >= 0.0001 || s.config.Limits.Cpu >= 0.0001) && !enableCgroup {
		// 至少有一个资源限制才会启用 Cgroup v2，不支持非 root 或 docker 环境
		log.DefaultLogger.Warning("[Sandbox] cgroup v2 resource limit not enabled, please check is running as root or in docker?", "id", s.id, "name", s.config.Name, "workDir", s.workDir, "mem", s.config.Limits.Mem, "swap", s.config.Limits.Swap, "cpu", s.config.Limits.Cpu)
	}

	// 4. 部署引导程序
	initFile := filepath.Join(s.root, "/init")
	os.WriteFile(initFile, initExec, 0755)

	// 启动引导进程 (注意：此时不再在 Go 层设置 Chroot，交由 init 处理)
	netEnabled := uint8(0)
	cloneFlags := syscall.CLONE_NEWNS | syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS
	if !s.config.Network.AllowInternet && !s.config.Network.AllowLocalNetwork && len(s.config.Network.AllowListen) == 0 && len(s.config.Network.AllowList) == 0 {
		cloneFlags |= syscall.CLONE_NEWNET
	} else {
		netEnabled = 1
	}
	cmdSysProcAttr := &syscall.SysProcAttr{
		Chroot:     s.root, // Go 依然需要先将进程禁锢在 root 目录下
		Cloneflags: uintptr(cloneFlags),
	}

	// 设置环境变量（支持变量引用）
	cmdEnv := make([]string, 0, len(initEnv))
	for k, v := range initEnv {
		cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v))
	}

	// 启动 init 进程
	var cmdStdin io.WriteCloser
	for i := 0; i < 10; i++ {
		time.Sleep(time.Millisecond)
		s.cmd = exec.Command("/init")
		s.cmd.SysProcAttr = cmdSysProcAttr
		s.cmd.Env = cmdEnv
		stdin, err := s.cmd.StdinPipe()
		if err != nil {
			s._cleanup()
			return nil, err
		}
		s.cmd.Stdout = os.Stdout
		s.cmd.Stderr = os.Stderr

		if err := s.cmd.Start(); err != nil {
			stdin.Close()
			if !errors.Is(err, syscall.ETXTBSY) {
				s._cleanup()
				return nil, err
			}
			if i >= 3 {
				s.log("init ETXTBSY", "retry", i)
			}
		} else {
			cmdStdin = stdin
			break
		}
	}
	if cmdStdin == nil {
		s._cleanup()
		return nil, fmt.Errorf("init start failed")
	}

	err := os.Remove(initFile)
	s.assertLog("remove init file", err, "id", s.id, "name", s.config.Name, "initFile", initFile)

	s.pid = s.cmd.Process.Pid

	// 6. 完成后续状态记录
	if enableCgroup {
		err := os.WriteFile(filepath.Join(cgPath, "cgroup.procs"), []byte(strconv.Itoa(s.pid)), 0644)
		s.assertLog(fmt.Sprintf("Write %s to %s", strconv.Itoa(s.pid), filepath.Join(cgPath, "cgroup.procs")), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
	}

	// 检查Cgroup
	// fmt.Println(u.BMagenta(filepath.Join(cgPath, "cpu.max")), u.ReadFileN(filepath.Join(cgPath, "cpu.max")))
	// fmt.Println(u.BMagenta(filepath.Join(cgPath, "memory.max")), u.ReadFileN(filepath.Join(cgPath, "memory.max")))
	// fmt.Println(u.BMagenta(filepath.Join(cgPath, "memory.swap.max")), u.ReadFileN(filepath.Join(cgPath, "memory.swap.max")))
	// fmt.Println(u.BMagenta(filepath.Join(cgPath, "cgroup.procs")), u.ReadFileN(filepath.Join(cgPath, "cgroup.procs")))

	// 7. 按照协议发送配置信息 (LittleEndian, 匹配 C 端的 uint32_t)
	// 写入 UID & GID
	binary.Write(cmdStdin, binary.LittleEndian, uint32(s.uid))
	binary.Write(cmdStdin, binary.LittleEndian, uint32(s.gid))

	// 写入 WorkDir
	binary.Write(cmdStdin, binary.LittleEndian, uint32(len(s.config.WorkDir)))
	cmdStdin.Write([]byte(s.config.WorkDir))

	// 写入 Args
	allArgs := append([]string{s.config.StartCmd}, s.config.StartArgs...)
	binary.Write(cmdStdin, binary.LittleEndian, uint32(len(allArgs)))
	for _, arg := range allArgs {
		binary.Write(cmdStdin, binary.LittleEndian, uint32(len(arg)))
		cmdStdin.Write([]byte(arg))
	}

	// 写入网络配置总开关
	binary.Write(cmdStdin, binary.LittleEndian, netEnabled)
	if netEnabled == 1 {
		// 1. 基础开关 (各1字节)
		binary.Write(cmdStdin, binary.LittleEndian, u.If(s.config.Network.AllowInternet, uint8(1), uint8(0)))
		binary.Write(cmdStdin, binary.LittleEndian, u.If(s.config.Network.AllowLocalNetwork, uint8(1), uint8(0)))

		// 2. AllowListen (端口列表)
		binary.Write(cmdStdin, binary.LittleEndian, uint32(len(s.config.Network.AllowListen)))
		for _, port := range s.config.Network.AllowListen {
			binary.Write(cmdStdin, binary.LittleEndian, uint32(port))
		}

		// 3. 解析并发送 AllowList
		allowRules := []NetRule{}
		for _, s := range s.config.Network.AllowList {
			if r, err := ParseNetRule(s); err == nil {
				allowRules = append(allowRules, *r)
			}
		}
		binary.Write(cmdStdin, binary.LittleEndian, uint32(len(allowRules)))
		for _, r := range allowRules {
			binary.Write(cmdStdin, binary.LittleEndian, r)
		}

		// 4. 解析并发送 BlockList
		blockRules := []NetRule{}
		for _, s := range s.config.Network.BlockList {
			if r, err := ParseNetRule(s); err == nil {
				blockRules = append(blockRules, *r)
			}
		}
		binary.Write(cmdStdin, binary.LittleEndian, uint32(len(blockRules)))
		for _, r := range blockRules {
			binary.Write(cmdStdin, binary.LittleEndian, r)
		}
	}

	// 必须主动关闭，触发 EOF 并释放 fd
	cmdStdin.Close()

	s.status = "running"
	u.Save(filepath.Join(s.root, ".state.json"), State{
		Id:          s.id,
		Pid:         s.pid,
		StartTime:   s.startTime,
		WorkDir:     s.workDir,
		MountedList: s.mountedList,
	})
	RegisterSandbox(s)
	return s, nil
}

func (s *Sandbox) Kill() error {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.status != "running" && s.status != "starting" {
		return gojs.Err("sandbox not running or starting")
	}
	s.status = "stopping"
	return s._kill(syscall.SIGTERM)
}

func (s *Sandbox) Wait(timeout int64) (any, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.status != "running" && s.status != "stopping" {
		return nil, gojs.Err("sandbox not running or stopping")
	}

	if s._alive() {
		ch := make(chan error, 1)
		go func() {
			state, err := s._waitProcess()
			if err == nil && state != nil {
				s.status = "exited"
			}
			ch <- err
		}()

		select {
		case _ = <-ch:
			break
		case <-time.After(time.Duration(timeout) * time.Millisecond):
			s._kill(syscall.SIGKILL)
			break
		}
	}

	return s._cleanup()
}

func (s *Sandbox) Cleanup() (any, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s._cleanup()
}

func (s *Sandbox) _cleanup() (any, error) {
	s.log("cleaning up sandbox", "id", s.id, "name", s.config.Name, "workDir", s.workDir)
	cgPath := "/sys/fs/cgroup/" + s.id
	if u.FileExists(cgPath) {
		killPath := filepath.Join(cgPath, "cgroup.kill")
		if _, err := os.Stat(killPath); err == nil {
			_ = os.WriteFile(killPath, []byte("1"), 0644)
			time.Sleep(10 * time.Millisecond) // 稍等片刻让内核处理完毕
		}
		err := os.RemoveAll(cgPath)
		s.assertLog(fmt.Sprintf("Remove Cgroup %s", cgPath), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
	}

	for i := len(s.mountedList) - 1; i >= 0; i-- {
		err := syscall.Unmount(s.mountedList[i], syscall.MNT_DETACH)
		// 忽略 /proc 和 /sys 挂载失败提示
		if err == nil || (!strings.HasSuffix(s.mountedList[i], "/proc") && !strings.HasSuffix(s.mountedList[i], "/sys")) {
			s.assertLog(fmt.Sprintf("Unmount %s", s.mountedList[i]), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
		}
	}

	_, err := copyLog(s.workDir, s.startTime)
	s.assertLog("Copy log to logs directory", err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
	outLog := u.ReadFileN(filepath.Join(s.workDir, "stdout.log"))
	errLog := u.ReadFileN(filepath.Join(s.workDir, "stderr.log"))

	err = os.RemoveAll(s.root)
	s.assertLog(fmt.Sprintf("Remove Sandbox %s", s.root), err, "id", s.id, "name", s.config.Name, "workDir", s.workDir)
	releaseId(s.id)
	ReleaseSandbox(s.id)

	var outData any = outLog
	if strings.HasPrefix(outLog, "{") && strings.HasSuffix(outLog, "}") || strings.HasPrefix(outLog, "[") && strings.HasSuffix(outLog, "]") {
		var data map[string]any
		if err := json.Unmarshal([]byte(outLog), &data); err == nil {
			outData = data
		}
	}

	if errLog != "" {
		return outData, errors.New(errLog)
	}
	return outData, nil
}

// 建议在 struct 中增加字段记录上次采样，或者由调用方维护
// 这里提供一个逻辑更健壮的单次查询版本

func (s *Sandbox) Status() Status {
	st := Status{
		Id:        s.id,
		Pid:       s.pid,
		Alive:     s._alive(),
		Status:    s.status,
		StartTime: s.startTime,
	}

	// 统一处理 Uptime，避免除零错误
	now := time.Now().Unix()
	st.Uptime = now - s.startTime
	if st.Uptime <= 0 {
		st.Uptime = 1
	}

	cgPath := "/sys/fs/cgroup/" + s.id
	hasCgroup := u.FileExists(cgPath)

	// 1. 内存统计
	if hasCgroup {
		if data, err := os.ReadFile(filepath.Join(cgPath, "memory.current")); err == nil {
			usage, _ := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
			st.MemoryUsage = uint(usage / 1024 / 1024)
		}
	} else {
		// 非 Cgroup 模式下，读取 VmRSS (物理内存占用)，比 statm 更直观
		if data, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", st.Pid)); err == nil {
			lines := strings.Split(string(data), "\n")
			for _, line := range lines {
				if strings.HasPrefix(line, "VmRSS:") {
					fields := strings.Fields(line)
					if len(fields) >= 2 {
						val, _ := strconv.ParseUint(fields[1], 10, 64)
						st.MemoryUsage = uint(val / 1024) // status 里通常是 KB
					}
					break
				}
			}
		}
	}

	// 2. CPU 统计
	if hasCgroup {
		if data, err := os.ReadFile(filepath.Join(cgPath, "cpu.stat")); err == nil {
			for _, line := range strings.Split(string(data), "\n") {
				if strings.HasPrefix(line, "usage_usec") {
					parts := strings.Fields(line)
					if len(parts) >= 2 {
						usageUsec, _ := strconv.ParseFloat(parts[1], 64)
						// 计算全生命周期平均负载
						// 如果要瞬时负载，需在外部存储上次的 usageUsec 和时间戳做 delta
						st.CpuUsage = (usageUsec / (float64(st.Uptime) * 1000000.0)) * 100
					}
					break
				}
			}
		}
	} else {
		if data, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", st.Pid)); err == nil {
			fields := strings.Fields(string(data))
			if len(fields) > 14 {
				// utime(14) + stime(15)
				utime, _ := strconv.ParseFloat(fields[13], 64)
				stime, _ := strconv.ParseFloat(fields[14], 64)

				// 这里的 100 是单核系数，多核环境下建议除以 runtime.NumCPU()
				totalSec := (utime + stime) / 100.0
				st.CpuUsage = (totalSec / float64(st.Uptime)) * 100
			}
		}
	}

	// 修正：如果 CPU 计算结果超过 100% (多核情况)，保持原样展示或按需缩放
	return st
}
