//go:build linux || darwin

package runtimeproc

import (
	"context"
	"errors"
	"log/slog"
	"os"
	"syscall"
)

func (m platformManager) KillTree(ctx context.Context, pid int) error {
	processes, err := m.ListProcesses(ctx)
	if err != nil {
		return err
	}
	children := childPIDMap(processes)
	for _, child := range descendantPIDs(pid, children) {
		if err := killPID(child); err != nil {
			slog.Debug("failed to kill runtime child process",
				"pid", child,
				"root_pid", pid,
				"error", err,
			)
		}
	}
	return killPID(pid)
}

func childPIDMap(processes []ProcessInfo) map[int][]int {
	children := make(map[int][]int)
	for _, proc := range processes {
		if proc.PID > 0 && proc.PPID > 0 {
			children[proc.PPID] = append(children[proc.PPID], proc.PID)
		}
	}
	return children
}

func descendantPIDs(pid int, children map[int][]int) []int {
	var result []int
	var visit func(int)
	visit = func(parent int) {
		for _, child := range children[parent] {
			result = append(result, child)
			visit(child)
		}
	}
	visit(pid)
	for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
		result[i], result[j] = result[j], result[i]
	}
	return result
}

func killPID(pid int) error {
	if pid <= 0 || pid == os.Getpid() {
		return nil
	}
	err := syscall.Kill(pid, syscall.SIGKILL)
	if err == nil || errors.Is(err, syscall.ESRCH) {
		return nil
	}
	return err
}
