// Copyright (c) 2020-2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package fx

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
)

// DefaultTimeout is the default timeout for starting or stopping an
// application. It can be configured with the [StartTimeout] and [StopTimeout]
// options.
const DefaultTimeout = 15 * time.Second

// An Option specifies the behavior of the application.
// This is the primary means by which you interface with Fx.
//
// Zero or more options are specified at startup with [New].
// Options cannot be changed once an application has been initialized.
// Options may be grouped into a single option using the [Options] function.
// A group of options providing a logical unit of functionality
// may use [Module] to name that functionality
// and scope certain operations to within that module.
type Option interface {
	fmt.Stringer

	apply(*module)
}

// Error registers any number of errors with the application to short-circuit
// startup. If more than one error is given, the errors are combined into a
// single error.
//
// Similar to invocations, errors are applied in order. All Provide and Invoke
// options registered before or after an Error option will not be applied.
func ( ...error) Option {
	return errorOption()
}

type errorOption []error

func ( errorOption) ( *module) {
	.app.err = multierr.Append(.app.err, multierr.Combine(...))
}

func ( errorOption) () string {
	return fmt.Sprintf("fx.Error(%v)", multierr.Combine(...))
}

// Options bundles a group of options together into a single option.
//
// Use Options to group together options that don't belong in a [Module].
//
//	var loggingAndMetrics = fx.Options(
//		logging.Module,
//		metrics.Module,
//		fx.Invoke(func(logger *log.Logger) {
//			app.globalLogger = logger
//		}),
//	)
func ( ...Option) Option {
	return optionGroup()
}

type optionGroup []Option

func ( optionGroup) ( *module) {
	for ,  := range  {
		.apply()
	}
}

func ( optionGroup) () string {
	 := make([]string, len())
	for ,  := range  {
		[] = fmt.Sprint()
	}
	return fmt.Sprintf("fx.Options(%s)", strings.Join(, ", "))
}

// StartTimeout changes the application's start timeout.
// This controls the total time that all [OnStart] hooks have to complete.
// If the timeout is exceeded, the application will fail to start.
//
// Defaults to [DefaultTimeout].
func ( time.Duration) Option {
	return startTimeoutOption()
}

type startTimeoutOption time.Duration

func ( startTimeoutOption) ( *module) {
	if .parent != nil {
		.app.err = fmt.Errorf("fx.StartTimeout Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		.app.startTimeout = time.Duration()
	}
}

func ( startTimeoutOption) () string {
	return fmt.Sprintf("fx.StartTimeout(%v)", time.Duration())
}

// StopTimeout changes the application's stop timeout.
// This controls the total time that all [OnStop] hooks have to complete.
// If the timeout is exceeded, the application will exit early.
//
// Defaults to [DefaultTimeout].
func ( time.Duration) Option {
	return stopTimeoutOption()
}

type stopTimeoutOption time.Duration

func ( stopTimeoutOption) ( *module) {
	if .parent != nil {
		.app.err = fmt.Errorf("fx.StopTimeout Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		.app.stopTimeout = time.Duration()
	}
}

func ( stopTimeoutOption) () string {
	return fmt.Sprintf("fx.StopTimeout(%v)", time.Duration())
}

// RecoverFromPanics causes panics that occur in functions given to [Provide],
// [Decorate], and [Invoke] to be recovered from.
// This error can be retrieved as any other error, by using (*App).Err().
func () Option {
	return recoverFromPanicsOption{}
}

type recoverFromPanicsOption struct{}

func ( recoverFromPanicsOption) ( *module) {
	if .parent != nil {
		.app.err = fmt.Errorf("fx.RecoverFromPanics Option should be passed to top-level " +
			"App, not to fx.Module")
	} else {
		.app.recoverFromPanics = true
	}
}

func ( recoverFromPanicsOption) () string {
	return "fx.RecoverFromPanics()"
}

// WithLogger specifies the [fxevent.Logger] used by Fx to log its own events
// (e.g. a constructor was provided, a function was invoked, etc.).
//
// The argument to this is a constructor with one of the following return
// types:
//
//	fxevent.Logger
//	(fxevent.Logger, error)
//
// The constructor may depend on any other types provided to the application.
// For example,
//
//	WithLogger(func(logger *zap.Logger) fxevent.Logger {
//	  return &fxevent.ZapLogger{Logger: logger}
//	})
//
// If specified, Fx will construct the logger and log all its events to the
// specified logger.
//
// If Fx fails to build the logger, or no logger is specified, it will fall back to
// [fxevent.ConsoleLogger] configured to write to stderr.
func ( interface{}) Option {
	return withLoggerOption{
		constructor: ,
		Stack:       fxreflect.CallerStack(1, 0),
	}
}

type withLoggerOption struct {
	constructor interface{}
	Stack       fxreflect.Stack
}

func ( withLoggerOption) ( *module) {
	.logConstructor = &provide{
		Target: .constructor,
		Stack:  .Stack,
	}
}

func ( withLoggerOption) () string {
	return fmt.Sprintf("fx.WithLogger(%s)", fxreflect.FuncName(.constructor))
}

// Printer is the interface required by Fx's logging backend. It's implemented
// by most loggers, including the one bundled with the standard library.
//
// Note, this will be deprecated in a future release.
// Prefer to use [fxevent.Logger] instead.
type Printer interface {
	Printf(string, ...interface{})
}

// Logger redirects the application's log output to the provided printer.
//
// Prefer to use [WithLogger] instead.
func ( Printer) Option {
	return loggerOption{}
}

type loggerOption struct{ p Printer }

func ( loggerOption) ( *module) {
	if .parent != nil {
		.app.err = fmt.Errorf("fx.Logger Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		 := writerFromPrinter(.p)
		.log = fxlog.DefaultLogger() // assuming np is thread-safe.
	}
}

func ( loggerOption) () string {
	return fmt.Sprintf("fx.Logger(%v)", .p)
}

// NopLogger disables the application's log output.
//
// Note that this makes some failures difficult to debug,
// since no errors are printed to console.
// Prefer to log to an in-memory buffer instead.
var NopLogger = WithLogger(func() fxevent.Logger { return fxevent.NopLogger })

// An App is a modular application built around dependency injection. Most
// users will only need to use the New constructor and the all-in-one Run
// convenience method. In more unusual cases, users may need to use the Err,
// Start, Done, and Stop methods by hand instead of relying on Run.
//
// [New] creates and initializes an App. All applications begin with a
// constructor for the Lifecycle type already registered.
//
// In addition to that built-in functionality, users typically pass a handful
// of [Provide] options and one or more [Invoke] options. The Provide options
// teach the application how to instantiate a variety of types, and the Invoke
// options describe how to initialize the application.
//
// When created, the application immediately executes all the functions passed
// via Invoke options. To supply these functions with the parameters they
// need, the application looks for constructors that return the appropriate
// types; if constructors for any required types are missing or any
// invocations return an error, the application will fail to start (and Err
// will return a descriptive error message).
//
// Once all the invocations (and any required constructors) have been called,
// New returns and the application is ready to be started using Run or Start.
// On startup, it executes any OnStart hooks registered with its Lifecycle.
// OnStart hooks are executed one at a time, in order, and must all complete
// within a configurable deadline (by default, 15 seconds). For details on the
// order in which OnStart hooks are executed, see the documentation for the
// Start method.
//
// At this point, the application has successfully started up. If started via
// Run, it will continue operating until it receives a shutdown signal from
// Done (see the [App.Done] documentation for details); if started explicitly via
// Start, it will operate until the user calls Stop. On shutdown, OnStop hooks
// execute one at a time, in reverse order, and must all complete within a
// configurable deadline (again, 15 seconds by default).
type App struct {
	err       error
	clock     fxclock.Clock
	lifecycle *lifecycleWrapper

	container *dig.Container
	root      *module

	// Timeouts used
	startTimeout time.Duration
	stopTimeout  time.Duration
	// Decides how we react to errors when building the graph.
	errorHooks []ErrorHandler
	validate   bool
	// Whether to recover from panics in Dig container
	recoverFromPanics bool

	// Used to signal shutdowns.
	receivers signalReceivers

	osExit func(code int) // os.Exit override; used for testing only
}

// provide is a single constructor provided to Fx.
type provide struct {
	// Constructor provided to Fx. This may be an fx.Annotated.
	Target interface{}

	// Stack trace of where this provide was made.
	Stack fxreflect.Stack

	// IsSupply is true when the Target constructor was emitted by fx.Supply.
	IsSupply   bool
	SupplyType reflect.Type // set only if IsSupply

	// Set if the type should be provided at private scope.
	Private bool
}

// invoke is a single invocation request to Fx.
type invoke struct {
	// Function to invoke.
	Target interface{}

	// Stack trace of where this invoke was made.
	Stack fxreflect.Stack
}

// ErrorHandler handles Fx application startup errors.
// Register these with [ErrorHook].
// If specified, and the application fails to start up,
// the failure will still cause a crash,
// but you'll have a chance to log the error or take some other action.
type ErrorHandler interface {
	HandleError(error)
}

// ErrorHook registers error handlers that implement error handling functions.
// They are executed on invoke failures. Passing multiple ErrorHandlers appends
// the new handlers to the application's existing list.
func ( ...ErrorHandler) Option {
	return errorHookOption()
}

type errorHookOption []ErrorHandler

func ( errorHookOption) ( *module) {
	.app.errorHooks = append(.app.errorHooks, ...)
}

func ( errorHookOption) () string {
	 := make([]string, len())
	for ,  := range  {
		[] = fmt.Sprint()
	}
	return fmt.Sprintf("fx.ErrorHook(%v)", strings.Join(, ", "))
}

type errorHandlerList []ErrorHandler

func ( errorHandlerList) ( error) {
	for ,  := range  {
		.HandleError()
	}
}

// validate sets *App into validation mode without running invoked functions.
func validate( bool) Option {
	return &validateOption{
		validate: ,
	}
}

type validateOption struct {
	validate bool
}

func ( validateOption) ( *module) {
	if .parent != nil {
		.app.err = fmt.Errorf("fx.validate Option should be passed to top-level App, " +
			"not to fx.Module")
	} else {
		.app.validate = .validate
	}
}

func ( validateOption) () string {
	return fmt.Sprintf("fx.validate(%v)", .validate)
}

// ValidateApp validates that supplied graph would run and is not missing any dependencies. This
// method does not invoke actual input functions.
func ( ...Option) error {
	 = append(, validate(true))
	 := New(...)

	return .Err()
}

// New creates and initializes an App, immediately executing any functions
// registered via [Invoke] options. See the documentation of the App struct for
// details on the application's initialization, startup, and shutdown logic.
func ( ...Option) *App {
	 := fxlog.DefaultLogger(os.Stderr)

	 := &App{
		clock:        fxclock.System,
		startTimeout: DefaultTimeout,
		stopTimeout:  DefaultTimeout,
		receivers:    newSignalReceivers(),
	}
	.root = &module{
		app: ,
		// We start with a logger that writes to stderr. One of the
		// following three things can change this:
		//
		// - fx.Logger was provided to change the output stream
		// - fx.WithLogger was provided to change the logger
		//   implementation
		// - Both, fx.Logger and fx.WithLogger were provided
		//
		// The first two cases are straightforward: we use what the
		// user gave us. For the last case, however, we need to fall
		// back to what was provided to fx.Logger if fx.WithLogger
		// fails.
		log:   ,
		trace: []string{fxreflect.CallerStack(1, 2)[0].String()},
	}

	for ,  := range  {
		.apply(.root)
	}

	// There are a few levels of wrapping on the lifecycle here. To quickly
	// cover them:
	//
	// - lifecycleWrapper ensures that we don't unintentionally expose the
	//   Start and Stop methods of the internal lifecycle.Lifecycle type
	// - lifecycleWrapper also adapts the internal lifecycle.Hook type into
	//   the public fx.Hook type.
	// - appLogger ensures that the lifecycle always logs events to the
	//   "current" logger associated with the fx.App.
	.lifecycle = &lifecycleWrapper{
		lifecycle.New(appLogger{}, .clock),
	}

	 := []dig.Option{
		dig.DeferAcyclicVerification(),
		dig.DryRun(.validate),
	}

	if .recoverFromPanics {
		 = append(, dig.RecoverFromPanics())
	}

	.container = dig.New(...)
	.root.build(, .container)

	// Provide Fx types first to increase the chance a custom logger
	// can be successfully built in the face of unrelated DI failure.
	// E.g., for a custom logger that relies on the Lifecycle type.
	 := fxreflect.CallerStack(0, 0) // include New in the stack for default Provides
	.root.provide(provide{
		Target: func() Lifecycle { return .lifecycle },
		Stack:  ,
	})
	.root.provide(provide{Target: .shutdowner, Stack: })
	.root.provide(provide{Target: .dotGraph, Stack: })
	.root.provideAll()

	// Run decorators before executing any Invokes
	// (including the ones inside installAllEventLoggers).
	.err = multierr.Append(.err, .root.decorateAll())

	// If you are thinking about returning here after provides: do not (just yet)!
	// If a custom logger was being used, we're still buffering messages.
	// We'll want to flush them to the logger.

	// custom app logger will be initialized by the root module.
	.root.installAllEventLoggers()

	// This error might have come from the provide loop above. We've
	// already flushed to the custom logger, so we can return.
	if .err != nil {
		return 
	}

	if  := .root.invokeAll();  != nil {
		.err = 

		if dig.CanVisualizeError() {
			var  bytes.Buffer
			dig.Visualize(.container, &, dig.VisualizeError())
			 = errorWithGraph{
				graph: .String(),
				err:   ,
			}
		}
		errorHandlerList(.errorHooks).HandleError()
	}

	return 
}

func ( *App) () fxevent.Logger {
	return .root.log
}

// DotGraph contains a DOT language visualization of the dependency graph in
// an Fx application. It is provided in the container by default at
// initialization. On failure to build the dependency graph, it is attached
// to the error and if possible, colorized to highlight the root cause of the
// failure.
//
// Note that DotGraph does not yet recognize [Decorate] and [Replace].
type DotGraph string

type errWithGraph interface {
	Graph() DotGraph
}

type errorWithGraph struct {
	graph string
	err   error
}

func ( errorWithGraph) () DotGraph {
	return DotGraph(.graph)
}

func ( errorWithGraph) () string {
	return .err.Error()
}

// VisualizeError returns the visualization of the error if available.
//
// Note that VisualizeError does not yet recognize [Decorate] and [Replace].
func ( error) (string, error) {
	var  errWithGraph
	if errors.As(, &) {
		if  := .Graph();  != "" {
			return string(), nil
		}
	}
	return "", errors.New("unable to visualize error")
}

// Exits the application with the given exit code.
func ( *App) ( int) {
	 := os.Exit
	if .osExit != nil {
		 = .osExit
	}
	()
}

// Run starts the application, blocks on the signals channel, and then
// gracefully shuts the application down. It uses [DefaultTimeout] to set a
// deadline for application startup and shutdown, unless the user has
// configured different timeouts with the [StartTimeout] or [StopTimeout] options.
// It's designed to make typical applications simple to run.
// The minimal Fx application looks like this:
//
//	fx.New().Run()
//
// All of Run's functionality is implemented in terms of the exported
// Start, Done, and Stop methods. Applications with more specialized needs
// can use those methods directly instead of relying on Run.
//
// After the application has started,
// it can be shut down by sending a signal or calling [Shutdowner.Shutdown].
// On successful shutdown, whether initiated by a signal or by the user,
// Run will return to the caller, allowing it to exit cleanly.
// Run will exit with a non-zero status code
// if startup or shutdown operations fail,
// or if the [Shutdowner] supplied a non-zero exit code.
func ( *App) () {
	// Historically, we do not os.Exit(0) even though most applications
	// cede control to Fx with they call app.Run. To avoid a breaking
	// change, never os.Exit for success.
	if  := .run(.Wait);  != 0 {
		.exit()
	}
}

func ( *App) ( func() <-chan ShutdownSignal) ( int) {
	,  := .clock.WithTimeout(context.Background(), .StartTimeout())
	defer ()

	if  := .Start();  != nil {
		return 1
	}

	 := <-()
	.log().LogEvent(&fxevent.Stopping{Signal: .Signal})
	 = .ExitCode

	,  := .clock.WithTimeout(context.Background(), .StopTimeout())
	defer ()

	if  := .Stop();  != nil {
		return 1
	}

	return 
}

// Err returns any error encountered during New's initialization. See the
// documentation of the New method for details, but typical errors include
// missing constructors, circular dependencies, constructor errors, and
// invocation errors.
//
// Most users won't need to use this method, since both Run and Start
// short-circuit if initialization failed.
func ( *App) () error {
	return .err
}

var (
	_onStartHook = "OnStart"
	_onStopHook  = "OnStop"
)

// Start kicks off all long-running goroutines, like network servers or
// message queue consumers. It does this by interacting with the application's
// Lifecycle.
//
// By taking a dependency on the Lifecycle type, some of the user-supplied
// functions called during initialization may have registered start and stop
// hooks. Because initialization calls constructors serially and in dependency
// order, hooks are naturally registered in serial and dependency order too.
//
// Start executes all OnStart hooks registered with the application's
// Lifecycle, one at a time and in order. This ensures that each constructor's
// start hooks aren't executed until all its dependencies' start hooks
// complete. If any of the start hooks return an error, Start short-circuits,
// calls Stop, and returns the inciting error.
//
// Note that Start short-circuits immediately if the New constructor
// encountered any errors in application initialization.
func ( *App) ( context.Context) ( error) {
	defer func() {
		.log().LogEvent(&fxevent.Started{Err: })
	}()

	if .err != nil {
		// Some provides failed, short-circuit immediately.
		return .err
	}

	return withTimeout(, &withTimeoutParams{
		hook:      _onStartHook,
		callback:  .start,
		lifecycle: .lifecycle,
		log:       .log(),
	})
}

// withRollback will execute an anonymous function with a given context.
// if the anon func returns an error, rollback methods will be called and related events emitted
func ( *App) (
	 context.Context,
	 func(context.Context) error,
) error {
	if  := ();  != nil {
		.log().LogEvent(&fxevent.RollingBack{StartErr: })

		 := .lifecycle.Stop()
		.log().LogEvent(&fxevent.RolledBack{Err: })

		if  != nil {
			return multierr.Append(, )
		}

		return 
	}

	return nil
}

func ( *App) ( context.Context) error {
	return .withRollback(, func( context.Context) error {
		if  := .lifecycle.Start();  != nil {
			return 
		}
		return nil
	})
}

// Stop gracefully stops the application. It executes any registered OnStop
// hooks in reverse order, so that each constructor's stop hooks are called
// before its dependencies' stop hooks.
//
// If the application didn't start cleanly, only hooks whose OnStart phase was
// called are executed. However, all those hooks are executed, even if some
// fail.
func ( *App) ( context.Context) ( error) {
	defer func() {
		.log().LogEvent(&fxevent.Stopped{Err: })
	}()

	 := func( context.Context) error {
		defer .receivers.Stop()
		return .lifecycle.Stop()
	}

	return withTimeout(, &withTimeoutParams{
		hook:      _onStopHook,
		callback:  ,
		lifecycle: .lifecycle,
		log:       .log(),
	})
}

// Done returns a channel of signals to block on after starting the
// application. Applications listen for the SIGINT and SIGTERM signals; during
// development, users can send the application SIGTERM by pressing Ctrl-C in
// the same terminal as the running process.
//
// Alternatively, a signal can be broadcast to all done channels manually by
// using the Shutdown functionality (see the [Shutdowner] documentation for details).
func ( *App) () <-chan os.Signal {
	.receivers.Start() // No-op if running
	return .receivers.Done()
}

// Wait returns a channel of [ShutdownSignal] to block on after starting the
// application and function, similar to [App.Done], but with a minor difference:
// if the app was shut down via [Shutdowner.Shutdown],
// the exit code (if provied via [ExitCode]) will be available
// in the [ShutdownSignal] struct.
// Otherwise, the signal that was received will be set.
func ( *App) () <-chan ShutdownSignal {
	.receivers.Start() // No-op if running
	return .receivers.Wait()
}

// StartTimeout returns the configured startup timeout.
// This defaults to [DefaultTimeout], and can be changed with the
// [StartTimeout] option.
func ( *App) () time.Duration {
	return .startTimeout
}

// StopTimeout returns the configured shutdown timeout.
// This defaults to [DefaultTimeout], and can be changed with the
// [StopTimeout] option.
func ( *App) () time.Duration {
	return .stopTimeout
}

func ( *App) () (DotGraph, error) {
	var  bytes.Buffer
	 := dig.Visualize(.container, &)
	return DotGraph(.String()), 
}

type withTimeoutParams struct {
	log       fxevent.Logger
	hook      string
	callback  func(context.Context) error
	lifecycle *lifecycleWrapper
}

// errHookCallbackExited is returned when a hook callback does not finish executing
var errHookCallbackExited = errors.New("goroutine exited without returning")

func withTimeout( context.Context,  *withTimeoutParams) error {
	 := make(chan error, 1)
	go func() {
		// If runtime.Goexit() is called from within the callback
		// then nothing is written to the chan.
		// However the defer will still be called, so we can write to the chan,
		// to avoid hanging until the timeout is reached.
		 := false
		defer func() {
			if ! {
				 <- errHookCallbackExited
			}
		}()

		 <- .callback()
		 = true
	}()

	var  error

	select {
	case <-.Done():
		 = .Err()
	case  = <-:
		// If the context finished at the same time as the callback
		// prefer the context error.
		// This eliminates non-determinism in select-case selection.
		if .Err() != nil {
			 = .Err()
		}
	}

	return 
}

// appLogger logs events to the given Fx app's "current" logger.
//
// Use this with lifecycle, for example, to ensure that events always go to the
// correct logger.
type appLogger struct{ app *App }

func ( appLogger) ( fxevent.Event) {
	.app.log().LogEvent()
}