package node

import (
	
	
	
	

	

	
	amhelp 
	am 
	
	
	ampipe 
)

type SupervisorOpts struct {
	// InstanceNum is the number of this instance in a failsafe config, used for
	// the ID.
	InstanceNum int
	// Parent is a parent state machine for a new Supervisor state machine. See
	// [am.Opts].
	Parent am.Api
	// TODO
	Tags []string
}

// bootstrap is a bootstrap machine for a worker to connect to the supervisor,
// after forking.
//
// Flow: Start to WorkerLocalAddr.
type bootstrap struct {
	*am.ExceptionHandler
	Mach  *am.Machine
	Super *Supervisor

	// Name is the name of this bootstrap.
	Name       string
	LogEnabled bool

	server *rpc.Server
}

func newBootstrap( context.Context,  *Supervisor) (*bootstrap, error) {
	 := &bootstrap{
		Super:      ,
		Name:       .Name + utils.RandId(6),
		LogEnabled: os.Getenv(EnvAmNodeLogSupervisor) != "",
	}
	,  := am.NewCommon(, "nb-"+.Name, states.BootstrapSchema,
		ssB.Names(), , .Mach, &am.Opts{Tags: []string{"node-bootstrap"}})
	if  != nil {
		return nil, 
	}

	// prefix the random ID
	.Mach = 
	amhelp.MachDebugEnv()

	return , nil
}

func ( *bootstrap) ( *am.Event) {
	var  error
	 := .Mach.NewStateCtx(ssB.Start)

	// init rpc
	.server,  = rpc.NewServer(, "localhost:0", "nb-"+.Name, .Mach,
		&rpc.ServerOpts{Parent: .Mach})
	if  != nil {
		.Mach.AddErrState(ssB.ErrNetwork, , nil)
		return
	}
	amhelp.MachDebugEnv(.server.Mach)
	 = ampipe.BindErr(.server.Mach, .Mach, ssB.ErrNetwork)
	if  != nil {
		.Mach.AddErr(, nil)
		return
	}

	// start
	.server.Start()

	// timeout
	go func() {
		amhelp.Wait(, .Super.ConnTimeout)
		if !.Mach.IsDisposed() && .Mach.Not1(ssB.WorkerAddr) {
			 := fmt.Errorf("worker bootstrap: %w", am.ErrTimeout)
			.Super.Mach.AddErrState(ssS.ErrWorker, , Pass(&A{
				Bootstrap: ,
				Id:        .Mach.Id(),
			}))
		}
	}()
}

func ( *bootstrap) ( *am.Event) {
	if .server != nil {
		.server.Stop(true)
	}
	.Mach.Dispose()
}

func ( *bootstrap) ( *am.Event) bool {
	 := ParseArgs(.Args)
	return  != nil && .LocalAddr != "" && .PublicAddr != "" && .Id != ""
}

func ( *bootstrap) ( *am.Event) {
	 := ParseArgs(.Args)
	// copy
	 := *
	.BootAddr = .Addr()
	.log("worker addr %s: %s / %s", .Id, .PublicAddr, .LocalAddr)
	// pass the conn info to the supervisor, and self destruct
	.Super.Mach.Add1(ssS.WorkerConnected, Pass(&))

	// dispose after a while
	go func() {
		time.Sleep(1 * time.Second)
		.Mach.Remove1(ssB.Start, nil)
	}()
}

func ( *bootstrap) () {
	// TODO send bye to rpc-c
	.log("disposing bootstrap")
	.Mach.Remove1(ssB.Start, nil)
}

// Addr returns the address of the bootstrap server.
func ( *bootstrap) () string {
	if .server == nil {
		return ""
	}

	return .server.Addr
}

func ( *bootstrap) ( string,  ...any) {
	if !.LogEnabled {
		return
	}
	.Mach.Log(, ...)
}

// workerInfo is supervisor's data for a remote node worker.
type workerInfo struct {
	// proc is the OS process of this node worker
	proc *os.Process
	// rpc is the RPC client for this node worker
	rpc *rpc.Client
	// w is the RPC worker of this node worker. It locally represents the remote
	// node worker.
	w          *rpc.Worker
	publicAddr string
	localAddr  string
	errs       *cache.Cache
	errsRecent *cache.Cache
}

func newWorkerInfo( *Supervisor,  *os.Process) *workerInfo {
	return &workerInfo{
		errs:       cache.New(.WorkerErrTtl, time.Minute),
		errsRecent: cache.New(.WorkerErrRecent, time.Minute),
		proc:       ,
	}
}

func ( *workerInfo) () bool {
	return .errsRecent.ItemCount() > 0
}