// master.go v1.0

package sandbox

import (
	"errors"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/ssgo/log"
	"github.com/ssgo/u"
)

type SandboxStore struct {
	Id     string
	Config *Config
}

var sandboxStoreList = map[string]SandboxStore{}
var sandboxStoreFile = ""

func Restore(id string, cfg *Config) *Sandbox {
	root := filepath.Join(pluginConfig.Root, id)
	stateFile := filepath.Join(root, ".state.json")

	st := State{}
	if u.FileExists(stateFile) && u.Load(stateFile, &st) == nil {
		sb := &Sandbox{
			id:          id,
			root:        root,
			workDir:     st.WorkDir,
			config:      cfg,
			pid:         st.Pid,
			status:      "running",
			startTime:   st.StartTime,
			mountedList: st.MountedList,
		}
		// 检查进程是否存在
		if sb.Alive() {
			log.DefaultLogger.Info("[Sandbox] resumed existing process", "id", id, "pid", st.Pid, "name", cfg.Name)
			return sb
		} else {
			log.DefaultLogger.Info("[Sandbox] process dead, cleaning up residue", "id", id, "pid", st.Pid, "name", cfg.Name)
			sb.Cleanup()
		}
	}

	if cfg.AutoStart {
		log.DefaultLogger.Info("[Sandbox] auto starting process", "name", cfg.Name)
		sb, err := Start(cfg)
		if err != nil {
			log.DefaultLogger.Error("[Sandbox] auto-start failed", "err", err)
			return nil
		}
		return sb
	} else {
		// 不自动启动，返回 nil
		return nil
	}
}

func Query(name string) []*Sandbox {
	sandboxLock.RLock()
	defer sandboxLock.RUnlock()
	var list []*Sandbox
	for _, sb := range sandboxList {
		if sb.config.Name == name {
			list = append(list, sb)
		}
	}
	return list
}

func Fetch(id string) *Sandbox {
	sandboxLock.RLock()
	defer sandboxLock.RUnlock()
	for _, sb := range sandboxList {
		if sb.id == id {
			return sb
		}
	}
	return nil
}

func RegisterSandbox(sb *Sandbox) {
	sandboxLock.Lock()
	defer sandboxLock.Unlock()
	sandboxList[sb.id] = sb
	sandboxStoreList[sb.id] = SandboxStore{
		Id:     sb.id,
		Config: sb.config,
	}
	u.Save(sandboxStoreFile, sandboxStoreList)
}

func ReleaseSandbox(id string) {
	sandboxLock.Lock()
	defer sandboxLock.Unlock()
	delete(sandboxList, id)
	delete(sandboxStoreList, id)
	u.Save(sandboxStoreFile, sandboxStoreList)
}

func checkSandboxStatusTask() {
	for isRunning.Load() {
		for i := 0; i < 2*60; i++ {
			time.Sleep(time.Millisecond * 500)
			if !isRunning.Load() {
				return
			}
		}
		sblist := map[string]*Sandbox{}
		sandboxLock.RLock()
		for id, sb := range sandboxList {
			sblist[id] = sb
		}
		sandboxLock.RUnlock()
		for _, sb := range sblist {
			if !sb.Alive() && sb.status == "running" {
				sb.Cleanup()
			}
			if !isRunning.Load() {
				return
			}
		}
	}
}

func (sb *Sandbox) _kill(sig os.Signal) error {
	proc, err := os.FindProcess(sb.pid)
	if err == nil {
		err = proc.Signal(sig)
	}
	return err
}

func (sb *Sandbox) Alive() bool {
	sb.lock.Lock()
	defer sb.lock.Unlock()
	return sb._alive()
}

func (sb *Sandbox) _alive() bool {
	proc, err := os.FindProcess(sb.pid)
	if err == nil && proc.Signal(syscall.Signal(0)) == nil {
		// 精确比对启动时间，防止 PID 回绕
		if fi := u.GetFileInfo(fmt.Sprintf("/proc/%d", sb.pid)); fi != nil {
			if math.Abs(float64(fi.ModTime.Unix()-sb.startTime)) > 5 {
				// 确认启动时间差大于 5 秒，断言并非同一个进程
				return false
			}
		}
		return true
	}
	return false
}

func (sb *Sandbox) _waitProcess() (*os.ProcessState, error) {
	if proc, err := os.FindProcess(sb.pid); err == nil {
		return proc.Wait()
	}
	return nil, errors.New("pid not found")
}
