package process

import (
	
	
)

// Leaf converts a “leaf” function to a runnable process function that accepts
// callback. It accepts an optional gracefulStop function to perform graceful
// shutdown. If the function is nil, the process will be terminated by context
// cancellation instead.
//
// The resulting Runnable returns first non-nil error from functions in the
// following order: callback, gracefulStop, runInForeground. That is, if both
// callback and gracefulStop return a non-nil error, the latter is ignored.
//
// Example (HTTP):
//
//  var lis net.Listener
//  var srv *http.Server
//
//  process.Leaf(
//    func(_ context.Context) error {
//      err := srv.Serve(lis)
//      if errors.Is(err, http.ErrServerClosed) {
//        return nil
//      }
//      return err
//    },
//    func(ctx context.Context) error {
//      err := srv.Shutdown(ctx)
//      if err != nil {
//        return srv.Close()
//      }
//      return nil
//    },
//  )
//
// Example (gRPC):
//
//  var lis net.Listener
//  var srv *grpc.Server
//
//  process.Leaf(
//    func(_ context.Context) error {
//      return srv.Serve(lis)
//    },
//    func(ctx context.Context) error {
//      done := make(chan struct{})
//      go func() {
//        srv.GracefulStop()
//        close(done)
//      }()
//      select {
//      case <-ctx.Done():
//        srv.Stop()
//        <-done
//      case <-done:
//      }
//      return nil
//    },
//  )
//
// Alternatively, use [go.pact.im/x/grpcprocess] package for gRPC.
//
func (,  func( context.Context) error) Runnable {
	return &leafRunnable{, }
}

type leafRunnable struct {
	runInForeground func(ctx context.Context) error
	gracefulStop    func(ctx context.Context) error
}

func ( *leafRunnable) ( context.Context,  Callback) error {
	,  := context.WithCancel()
	defer ()

	var  sync.WaitGroup
	.Add(1)

	var  error
	go func() {
		defer .Done()
		 = .runInForeground()
		() // cancel callback
	}()

	 := ()

	var  error
	if .gracefulStop != nil {
		 = .gracefulStop()
	}

	()
	.Wait()

	switch {
	case  != nil:
		return 
	case  != nil:
		return 
	}
	return 
}

// StartStop returns a Runnable instance for the pair of start/stop functions.
// The stop function should perform a graceful shutdown until a context expires,
// then proceed with a forced shutdown.
//
// The resulting Runnable returns either start error or the first non-nil error
// from callback and stop functions. If both callback and stop return a non-nil
// error, the latter is ignored.
func (,  func( context.Context) error) Runnable {
	return &startStopRunnable{, }
}

type startStopRunnable struct {
	startInBackground func(ctx context.Context) error
	gracefulStop      func(ctx context.Context) error
}

func ( *startStopRunnable) ( context.Context,  Callback) error {
	if  := .startInBackground();  != nil {
		return 
	}
	 := ()
	if  := .gracefulStop();  != nil &&  == nil {
		return 
	}
	return 
}