// Copyright (c) 2022 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 (
	
	
	

	
	
)

// Decorate specifies one or more decorator functions to an Fx application.
//
// # Decorator functions
//
// Decorator functions let users augment objects in the graph.
// They can take in zero or more dependencies that must be provided to the
// application with fx.Provide, and produce one or more values that can be used
// by other fx.Provide and fx.Invoke calls.
//
//	fx.Decorate(func(log *zap.Logger) *zap.Logger {
//	  return log.Named("myapp")
//	})
//	fx.Invoke(func(log *zap.Logger) {
//	  log.Info("hello")
//	  // Output:
//	  // {"level": "info","logger":"myapp","msg":"hello"}
//	})
//
// The following decorator accepts multiple dependencies from the graph,
// augments and returns one of them.
//
//	fx.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger {
//	  return log.Named(cfg.Name)
//	})
//
// Similar to fx.Provide, functions passed to fx.Decorate may optionally return
// an error as their last result.
// If a decorator returns a non-nil error, it will halt application startup.
//
//	fx.Decorate(func(conn *sql.DB, cfg *Config) (*sql.DB, error) {
//	  if err := conn.Ping(); err != nil {
//	    return sql.Open("driver-name", cfg.FallbackDB)
//	  }
//	  return conn, nil
//	})
//
// Decorators support both, fx.In and fx.Out structs, similar to fx.Provide and
// fx.Invoke.
//
//	type Params struct {
//	  fx.In
//
//	  Client usersvc.Client `name:"readOnly"`
//	}
//
//	type Result struct {
//	  fx.Out
//
//	  Client usersvc.Client `name:"readOnly"`
//	}
//
//	fx.Decorate(func(p Params) Result {
//	  ...
//	})
//
// Decorators can be annotated with the fx.Annotate function, but not with the
// fx.Annotated type. Refer to documentation on fx.Annotate() to learn how to
// use it for annotating functions.
//
//	fx.Decorate(
//	  fx.Annotate(
//	    func(client usersvc.Client) usersvc.Client {
//	      // ...
//	    },
//	    fx.ParamTags(`name:"readOnly"`),
//	    fx.ResultTags(`name:"readOnly"`),
//	  ),
//	)
//
// Decorators support augmenting, filtering, or replacing value groups.
// To decorate a value group, expect the entire value group slice and produce
// the new slice.
//
//	type HandlerParam struct {
//	  fx.In
//
//	  Log      *zap.Logger
//	  Handlers []Handler `group:"server"
//	}
//
//	type HandlerResult struct {
//	  fx.Out
//
//	  Handlers []Handler `group:"server"
//	}
//
//	fx.Decorate(func(p HandlerParam) HandlerResult {
//	  var r HandlerResult
//	  for _, handler := range p.Handlers {
//	    r.Handlers = append(r.Handlers, wrapWithLogger(p.Log, handler))
//	  }
//	  return r
//	}),
//
// Decorators can not add new values to the graph,
// only modify or replace existing ones.
// Types returned by a decorator that are not already in the graph
// will be ignored.
//
// # Decorator scope
//
// Modifications made to the Fx graph with fx.Decorate are scoped to the
// deepest fx.Module inside which the decorator was specified.
//
//	fx.Module("mymodule",
//	  fx.Decorate(func(log *zap.Logger) *zap.Logger {
//	    return log.Named("myapp")
//	  }),
//	  fx.Invoke(func(log *zap.Logger) {
//	    log.Info("decorated logger")
//	    // Output:
//	    // {"level": "info","logger":"myapp","msg":"decorated logger"}
//	  }),
//	),
//	fx.Invoke(func(log *zap.Logger) {
//	  log.Info("plain logger")
//	  // Output:
//	  // {"level": "info","msg":"plain logger"}
//	}),
//
// Decorations specified in the top-level fx.New call apply across the
// application and chain with module-specific decorators.
//
//	fx.New(
//	  // ...
//	  fx.Decorate(func(log *zap.Logger) *zap.Logger {
//	    return log.With(zap.Field("service", "myservice"))
//	  }),
//	  // ...
//	  fx.Invoke(func(log *zap.Logger) {
//	    log.Info("outer decorator")
//	    // Output:
//	    // {"level": "info","service":"myservice","msg":"outer decorator"}
//	  }),
//	  // ...
//	  fx.Module("mymodule",
//	    fx.Decorate(func(log *zap.Logger) *zap.Logger {
//	      return log.Named("myapp")
//	    }),
//	    fx.Invoke(func(log *zap.Logger) {
//	      log.Info("inner decorator")
//	      // Output:
//	      // {"level": "info","logger":"myapp","service":"myservice","msg":"inner decorator"}
//	    }),
//	  ),
//	)
func ( ...interface{}) Option {
	return decorateOption{
		Targets: ,
		Stack:   fxreflect.CallerStack(1, 0),
	}
}

type decorateOption struct {
	Targets []interface{}
	Stack   fxreflect.Stack
}

func ( decorateOption) ( *module) {
	for ,  := range .Targets {
		.decorators = append(.decorators, decorator{
			Target: ,
			Stack:  .Stack,
		})
	}
}

func ( decorateOption) () string {
	 := make([]string, len(.Targets))
	for ,  := range .Targets {
		[] = fxreflect.FuncName()
	}
	return fmt.Sprintf("fx.Decorate(%s)", strings.Join(, ", "))
}

// decorator is a single decorator used in Fx.
type decorator struct {
	// Decorator provided to Fx.
	Target interface{}

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

	// Whether this decorator was specified via fx.Replace
	IsReplace   bool
	ReplaceType reflect.Type // set only if IsReplace
}

func runDecorator( container,  decorator,  ...dig.DecorateOption) ( error) {
	 := .Target
	defer func() {
		if  != nil {
			 = fmt.Errorf("fx.Decorate(%v) from:\n%+vFailed: %w", , .Stack, )
		}
	}()

	switch decorator := .(type) {
	case annotated:
		if ,  := .Build();  == nil {
			 = .Decorate(, ...)
		}
	default:
		 = .Decorate(, ...)
	}
	return
}