package sandbox

import (
	"bytes"
	"fmt"
	"os/exec"
	"runtime"
	"sync"

	"github.com/ssgo/log"
)

var runtimeLocks sync.Map

// 锁管理
func getRuntimeLock(language, typ, name string) *sync.Mutex {
	l, _ := runtimeLocks.LoadOrStore(language+":"+typ+":"+name, &sync.Mutex{})
	return l.(*sync.Mutex)
}

type RuntimeConfig struct {
	Root      string
	HttpProxy string
	Mirror    string
}

type RuntimeSandboxConfig struct {
	StartCmd string
	Envs     map[string]string
}

type Runtime struct {
	Check func(runtimePath, venvPath, projectPath string, uid, gid int, cfg *RuntimeConfig) (*RuntimeSandboxConfig, error)
}

var runtimeList = make(map[string]*Runtime)
var runtimeListLock sync.RWMutex

func RegisterRuntime(name string, rt *Runtime) {
	runtimeListLock.Lock()
	defer runtimeListLock.Unlock()
	runtimeList[name] = rt
}

func RunCmdWithEnv(envs map[string]string, cmdDir string, uid, gid int, name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	cmd.Dir = cmdDir
	for k, v := range envs {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out
	if runtime.GOOS == "linux" && uid > 0 {
		applyCredential(cmd, uid, gid)
	}
	err := cmd.Run()
	if err != nil {
		log.DefaultLogger.Error("[Sandbox] RunCmd failed "+err.Error(), "name", name, "args", args)
	}
	return out.String(), err
}

func RunCmd(cmdDir string, uid, gid int, name string, args ...string) (string, error) {
	return RunCmdWithEnv(nil, cmdDir, uid, gid, name, args...)
}
