// Package app provides small process lifecycle primitives for service app packages.
package app

import (
	"context"
	"sync"
)

// BackgroundGroup owns a cancellable worker context and waits for all registered workers to finish.
type BackgroundGroup struct {
	ctx    context.Context
	cancel context.CancelFunc
	wg     sync.WaitGroup
	once   sync.Once
}

// NewBackground creates a worker group rooted at parent; nil parent means context.Background.
func NewBackground(parent context.Context) *BackgroundGroup {
	if parent == nil {
		parent = context.Background()
	}
	ctx, cancel := context.WithCancel(parent)
	return &BackgroundGroup{ctx: ctx, cancel: cancel}
}

// Context returns the shared worker context. Workers should stop when it is canceled.
func (g *BackgroundGroup) Context() context.Context {
	if g == nil || g.ctx == nil {
		return context.Background()
	}
	return g.ctx
}

// Go starts one worker and gives it the shared cancellable context.
func (g *BackgroundGroup) Go(run func(context.Context)) {
	if g == nil || run == nil {
		return
	}
	g.wg.Add(1)
	go func() {
		defer g.wg.Done()
		run(g.Context())
	}()
}

// Stop cancels the shared worker context. It is safe to call more than once.
func (g *BackgroundGroup) Stop() {
	if g == nil {
		return
	}
	g.once.Do(g.cancel)
}

// StopAndWait cancels the shared context and waits for all workers to return.
func (g *BackgroundGroup) StopAndWait() {
	if g == nil {
		return
	}
	g.Stop()
	g.wg.Wait()
}
