package supervisor

import (
	
	
	
	
)

const (
	restartInitialWait  = time.Second
	restartLoopInterval = time.Minute
	restartLoopWait     = 0
)

// restartInitial restores the last state from the underlying table.
func ( *Supervisor[, ]) ( context.Context) {
	_ = .restart(, restartInitialWait) // TODO: log error
}

// spawnRestartLoop spawns a restartLoop using the given context and returns
// a function that stops restart loop and waits completion.
func ( *Supervisor[, ]) ( context.Context) func() {
	,  := context.WithCancel()

	var  sync.WaitGroup
	.Add(1)
	go func() {
		defer .Done()
		defer ()
		.restartLoop()
	}()

	return func() {
		()
		.Wait()
	}
}

// restartLoop runs a loop that calls restart.
func ( *Supervisor[, ]) ( context.Context) {
	const  = restartLoopInterval

	 := .clock.Timer()
	defer .Stop()
	for {
		select {
		case <-.Done():
			return
		case <-.C():
		}
		_ = .restart(, restartLoopWait) // TODO: log error
		.Reset()
	}
}

// restart starts processes from the table that are not currently running.
//
// If wait duration is not zero, it waits until background startProcess calls
// complete. This allows ensuring that we restore at least partial state in
// restoreInitial before invoking Supervisor’s Run callback.
func ( *Supervisor[, ]) ( context.Context,  time.Duration) error {
	,  := .table.Iter()
	if  != nil {
		return fmt.Errorf("create iterator: %w", )
	}
	defer func() {
		if  := .Close();  != nil {
			_ =  // TODO: log error
		}
	}()

	var ,  sync.WaitGroup
	var ,  chan struct{}

	for .Next() {
		, ,  := .Get()
		if  != nil {
			_ =  // TODO: log error
			continue
		}
		 := .restartProcessInBackground(, , )
		if  > 0 {
			// Note that we cannot use timer.C here since there are multiple
			// consumers (each process we start) and timer sends the value
			// on channel only once (and never closes it).
			if  == nil {
				 = make(chan struct{})
				 = make(chan struct{})

				.Add(1)
				go func() {
					defer .Done()
					 := .clock.Timer()
					defer .Stop()
					select {
					case <-.Done():
						return
					case <-.C():
						close()
					case <-:
					}
				}()
				defer .Wait()
			}
			.Add(1)
			go func() {
				defer .Done()
				select {
				case <-.Done():
				case <-:
				case <-:
				}
			}()
		}
	}
	if  := .Err();  != nil {
		_ =  // TODO: log error
	}

	// Do not wait for timer if all goroutines under wg have finished. Note
	// that this must be run after the loop since we cannot use wg.Wait
	// concurrently with wg.Add.
	if  != nil {
		.Wait()
		close()
	}

	return nil
}

// restartProcessInBackground starts the process for the given key in the
// background. It returns a channel that is closed when startProcess call
// completes.
func ( *Supervisor[, ]) ( context.Context,  ,  ) <-chan struct{} {
	 := make(chan struct{})
	.wg.Add(1)
	go func() {
		defer .wg.Done()
		defer close()
		_, _ = .startProcess(, , ) // TODO: log error
	}()
	return 
}