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

import (
	
	
	
	

	
	
	
)

// A ProvideOption modifies the default behavior of Provide.
type ProvideOption interface {
	applyProvideOption(*provideOptions)
}

type provideOptions struct {
	Name           string
	Group          string
	Info           *ProvideInfo
	As             []interface{}
	Location       *digreflect.Func
	Exported       bool
	Callback       Callback
	BeforeCallback BeforeCallback
}

func ( *provideOptions) () error {
	if len(.Group) > 0 {
		if len(.Name) > 0 {
			return newErrInvalidInput(
				fmt.Sprintf("cannot use named values with value groups: name:%q provided with group:%q", .Name, .Group), nil)
		}
	}

	// Names must be representable inside a backquoted string. The only
	// limitation for raw string literals as per
	// https://golang.org/ref/spec#raw_string_lit is that they cannot contain
	// backquotes.
	if strings.ContainsRune(.Name, '`') {
		return newErrInvalidInput(
			fmt.Sprintf("invalid dig.Name(%q): names cannot contain backquotes", .Name), nil)
	}
	if strings.ContainsRune(.Group, '`') {
		return newErrInvalidInput(
			fmt.Sprintf("invalid dig.Group(%q): group names cannot contain backquotes", .Group), nil)
	}

	for ,  := range .As {
		 := reflect.TypeOf()

		if  == nil {
			return newErrInvalidInput("invalid dig.As(nil): argument must be a pointer to an interface", nil)
		}

		if .Kind() != reflect.Ptr {
			return newErrInvalidInput(
				fmt.Sprintf("invalid dig.As(%v): argument must be a pointer to an interface", ), nil)
		}

		 := .Elem()
		if .Kind() != reflect.Interface {
			return newErrInvalidInput(
				fmt.Sprintf("invalid dig.As(*%v): argument must be a pointer to an interface", ), nil)
		}
	}
	return nil
}

// Name is a ProvideOption that specifies that all values produced by a
// constructor should have the given name. See also the package documentation
// about Named Values.
//
// Given,
//
//	func NewReadOnlyConnection(...) (*Connection, error)
//	func NewReadWriteConnection(...) (*Connection, error)
//
// The following will provide two connections to the container: one under the
// name "ro" and the other under the name "rw".
//
//	c.Provide(NewReadOnlyConnection, dig.Name("ro"))
//	c.Provide(NewReadWriteConnection, dig.Name("rw"))
//
// This option cannot be provided for constructors which produce result
// objects.
func ( string) ProvideOption {
	return provideNameOption()
}

type provideNameOption string

func ( provideNameOption) () string {
	return fmt.Sprintf("Name(%q)", string())
}

func ( provideNameOption) ( *provideOptions) {
	.Name = string()
}

// Group is a ProvideOption that specifies that all values produced by a
// constructor should be added to the specified group. See also the package
// documentation about Value Groups.
//
// This option cannot be provided for constructors which produce result
// objects.
func ( string) ProvideOption {
	return provideGroupOption()
}

type provideGroupOption string

func ( provideGroupOption) () string {
	return fmt.Sprintf("Group(%q)", string())
}

func ( provideGroupOption) ( *provideOptions) {
	.Group = string()
}

// ID is a unique integer representing the constructor node in the dependency graph.
type ID int

// ProvideInfo provides information about the constructor's inputs and outputs
// types as strings, as well as the ID of the constructor supplied to the Container.
// It contains ID for the constructor, as well as slices of Input and Output types,
// which are Stringers that report the types of the parameters and results respectively.
type ProvideInfo struct {
	ID      ID
	Inputs  []*Input
	Outputs []*Output
}

// Input contains information on an input parameter of a function.
type Input struct {
	t           reflect.Type
	optional    bool
	name, group string
}

func ( *Input) () string {
	 := make([]string, 0, 3)
	 := .t.String()
	if .optional {
		 = append(, "optional")
	}
	if .name != "" {
		 = append(, fmt.Sprintf("name = %q", .name))
	}
	if .group != "" {
		 = append(, fmt.Sprintf("group = %q", .group))
	}

	if len() == 0 {
		return 
	}
	return fmt.Sprintf("%v[%v]", , strings.Join(, ", "))
}

// Output contains information on an output produced by a function.
type Output struct {
	t           reflect.Type
	name, group string
}

func ( *Output) () string {
	 := make([]string, 0, 2)
	 := .t.String()
	if .name != "" {
		 = append(, fmt.Sprintf("name = %q", .name))
	}
	if .group != "" {
		 = append(, fmt.Sprintf("group = %q", .group))
	}

	if len() == 0 {
		return 
	}
	return fmt.Sprintf("%v[%v]", , strings.Join(, ", "))
}

// FillProvideInfo is a ProvideOption that writes info on what Dig was able to get
// out of the provided constructor into the provided ProvideInfo.
func ( *ProvideInfo) ProvideOption {
	return fillProvideInfoOption{info: }
}

type fillProvideInfoOption struct{ info *ProvideInfo }

func ( fillProvideInfoOption) () string {
	return fmt.Sprintf("FillProvideInfo(%p)", .info)
}

func ( fillProvideInfoOption) ( *provideOptions) {
	.Info = .info
}

// As is a ProvideOption that specifies that the value produced by the
// constructor implements one or more other interfaces and is provided
// to the container as those interfaces.
//
// As expects one or more pointers to the implemented interfaces. Values
// produced by constructors will be then available in the container as
// implementations of all of those interfaces, but not as the value itself.
//
// For example, the following will make io.Reader and io.Writer available
// in the container, but not buffer.
//
//	c.Provide(newBuffer, dig.As(new(io.Reader), new(io.Writer)))
//
// That is, the above is equivalent to the following.
//
//	c.Provide(func(...) (io.Reader, io.Writer) {
//	  b := newBuffer(...)
//	  return b, b
//	})
//
// If used with dig.Name, the type produced by the constructor and the types
// specified with dig.As will all use the same name. For example,
//
//	c.Provide(newFile, dig.As(new(io.Reader)), dig.Name("temp"))
//
// The above is equivalent to the following.
//
//	type Result struct {
//	  dig.Out
//
//	  Reader io.Reader `name:"temp"`
//	}
//
//	c.Provide(func(...) Result {
//	  f := newFile(...)
//	  return Result{
//	    Reader: f,
//	  }
//	})
//
// This option cannot be provided for constructors which produce result
// objects.
func ( ...interface{}) ProvideOption {
	return provideAsOption()
}

type provideAsOption []interface{}

func ( provideAsOption) () string {
	 := bytes.NewBufferString("As(")
	for ,  := range  {
		if  > 0 {
			.WriteString(", ")
		}
		.WriteString(reflect.TypeOf().Elem().String())
	}
	.WriteString(")")
	return .String()
}

func ( provideAsOption) ( *provideOptions) {
	.As = append(.As, ...)
}

// LocationForPC is a ProvideOption which specifies an alternate function program
// counter address to be used for debug information. The package, name, file and
// line number of this alternate function address will be used in error messages
// and DOT graphs. This option is intended to be used with functions created
// with the reflect.MakeFunc method whose error messages are otherwise hard to
// understand
func ( uintptr) ProvideOption {
	return provideLocationOption{
		loc: digreflect.InspectFuncPC(),
	}
}

type provideLocationOption struct{ loc *digreflect.Func }

func ( provideLocationOption) () string {
	return fmt.Sprintf("LocationForPC(%v)", .loc)
}

func ( provideLocationOption) ( *provideOptions) {
	.Location = .loc
}

// Export is a ProvideOption which specifies that the provided function should
// be made available to all Scopes available in the application, regardless
// of which Scope it was provided from. By default, it is false.
//
// For example,
//
//	c := New()
//	s1 := c.Scope("child 1")
//	s2:= c.Scope("child 2")
//	s1.Provide(func() *bytes.Buffer { ... })
//
// does not allow the constructor returning *bytes.Buffer to be made available to
// the root Container c or its sibling Scope s2.
//
// With Export, you can make this constructor available to all the Scopes:
//
//	s1.Provide(func() *bytes.Buffer { ... }, Export(true))
func ( bool) ProvideOption {
	return provideExportOption{exported: }
}

type provideExportOption struct{ exported bool }

func ( provideExportOption) () string {
	return fmt.Sprintf("Export(%v)", .exported)
}

func ( provideExportOption) ( *provideOptions) {
	.Exported = .exported
}

// provider encapsulates a user-provided constructor.
type provider interface {
	// ID is a unique numerical identifier for this provider.
	ID() dot.CtorID

	// Order reports the order of this provider in the graphHolder.
	// This value is usually returned by the graphHolder.NewNode method.
	Order(*Scope) int

	// Location returns where this constructor was defined.
	Location() *digreflect.Func

	// ParamList returns information about the direct dependencies of this
	// constructor.
	ParamList() paramList

	// ResultList returns information about the values produced by this
	// constructor.
	ResultList() resultList

	// Calls the underlying constructor, reading values from the
	// containerStore as needed.
	//
	// The values produced by this provider should be submitted into the
	// containerStore.
	Call(containerStore) error

	CType() reflect.Type

	OrigScope() *Scope
}

// Provide teaches the container how to build values of one or more types and
// expresses their dependencies.
//
// The first argument of Provide is a function that accepts zero or more
// parameters and returns one or more results. The function may optionally
// return an error to indicate that it failed to build the value. This
// function will be treated as the constructor for all the types it returns.
// This function will be called AT MOST ONCE when a type produced by it, or a
// type that consumes this function's output, is requested via Invoke. If the
// same types are requested multiple times, the previously produced value will
// be reused.
//
// Provide accepts argument types or dig.In structs as dependencies, and
// separate return values or dig.Out structs for results.
func ( *Container) ( interface{},  ...ProvideOption) error {
	return .scope.Provide(, ...)
}

// Provide teaches the Scope how to build values of one or more types and
// expresses their dependencies.
//
// The first argument of Provide is a function that accepts zero or more
// parameters and returns one or more results. The function may optionally
// return an error to indicate that it failed to build the value. This
// function will be treated as the constructor for all the types it returns.
// This function will be called AT MOST ONCE when a type produced by it, or a
// type that consumes this function's output, is requested via Invoke. If the
// same types are requested multiple times, the previously produced value will
// be reused.
//
// Provide accepts argument types or dig.In structs as dependencies, and
// separate return values or dig.Out structs for results.
//
// When a constructor is Provided to a Scope, it will propagate this to any
// Scopes that are descendents, but not ancestors of this Scope.
// To provide a constructor to all the Scopes available, provide it to
// Container, which is the root Scope.
func ( *Scope) ( interface{},  ...ProvideOption) error {
	 := reflect.TypeOf()
	if  == nil {
		return newErrInvalidInput("can't provide an untyped nil", nil)
	}
	if .Kind() != reflect.Func {
		return newErrInvalidInput(
			fmt.Sprintf("must provide constructor function, got %v (type %v)", , ), nil)
	}

	var  provideOptions
	for ,  := range  {
		.applyProvideOption(&)
	}
	if  := .Validate();  != nil {
		return 
	}

	if  := .provide(, );  != nil {
		var  *digreflect.Func
		if .Location == nil {
			 = digreflect.InspectFunc()
		} else {
			 = .Location
		}

		return errProvide{
			Func:   ,
			Reason: ,
		}
	}
	return nil
}

func ( *Scope) ( interface{},  provideOptions) ( error) {
	// If Export option is provided to the constructor, this should be injected to the
	// root-level Scope (Container) to allow it to propagate to all other Scopes.
	 := 
	if .Exported {
		 = .rootScope()
	}

	// For all scopes affected by this change,
	// take a snapshot of the current graph state before
	// we start making changes to it as we may need to
	// undo them upon encountering errors.
	 := .appendSubscopes(nil)

	defer func( []*Scope) {
		if  != nil {
			for ,  := range  {
				.gh.Rollback()
			}
		}
	}()

	for ,  := range  {
		.gh.Snapshot()
	}

	,  := newConstructorNode(
		,
		,
		,
		constructorOptions{
			ResultName:     .Name,
			ResultGroup:    .Group,
			ResultAs:       .As,
			Location:       .Location,
			Callback:       .Callback,
			BeforeCallback: .BeforeCallback,
		},
	)
	if  != nil {
		return 
	}

	,  := .findAndValidateResults(.ResultList())
	if  != nil {
		return 
	}

	 := reflect.TypeOf()
	if len() == 0 {
		return newErrInvalidInput(
			fmt.Sprintf("%v must provide at least one non-error type", ), nil)
	}

	 := make(map[key][]*constructorNode)
	for  := range  {
		// Cache old providers before running cycle detection.
		[] = .providers[]
		.providers[] = append(.providers[], )
	}

	for ,  := range  {
		.isVerifiedAcyclic = false
		if .deferAcyclicVerification {
			continue
		}
		if ,  := graph.IsAcyclic(.gh); ! {
			// When a cycle is detected, recover the old providers to reset
			// the providers map back to what it was before this node was
			// introduced.
			for ,  := range  {
				.providers[] = 
			}

			return newErrInvalidInput("this function introduces a cycle", .cycleDetectedError())
		}
		.isVerifiedAcyclic = true
	}

	.nodes = append(.nodes, )

	// Record introspection info for caller if Info option is specified
	if  := .Info;  != nil {
		 := .ParamList().DotParam()
		 := .ResultList().DotResult()

		.ID = (ID)(.id)
		.Inputs = make([]*Input, len())
		.Outputs = make([]*Output, len())

		for ,  := range  {
			.Inputs[] = &Input{
				t:        .Type,
				optional: .Optional,
				name:     .Name,
				group:    .Group,
			}
		}

		for ,  := range  {
			.Outputs[] = &Output{
				t:     .Type,
				name:  .Name,
				group: .Group,
			}
		}
	}
	return nil
}

// Builds a collection of all result types produced by this constructor.
func ( *Scope) ( resultList) (map[key]struct{}, error) {
	var  error
	 := make(map[key]string)
	walkResult(, connectionVisitor{
		s:        ,
		err:      &,
		keyPaths: ,
	})

	if  != nil {
		return nil, 
	}

	 := make(map[key]struct{}, len())
	for  := range  {
		[] = struct{}{}
	}
	return , nil
}

// Visits the results of a node and compiles a collection of all the keys
// produced by that node.
type connectionVisitor struct {
	s *Scope

	// If this points to a non-nil value, we've already encountered an error
	// and should stop traversing.
	err *error

	// Map of keys provided to path that provided this. The path is a string
	// documenting which positional return value or dig.Out attribute is
	// providing this particular key.
	//
	// For example, "[0].Foo" indicates that the value was provided by the Foo
	// attribute of the dig.Out returned as the first result of the
	// constructor.
	keyPaths map[key]string

	// We track the path to the current result here. For example, this will
	// be, ["[1]", "Foo", "Bar"] when we're visiting Bar in,
	//
	//   func() (io.Writer, struct {
	//     dig.Out
	//
	//     Foo struct {
	//       dig.Out
	//
	//       Bar io.Reader
	//     }
	//   })
	currentResultPath []string
}

func ( connectionVisitor) ( resultObjectField) resultVisitor {
	.currentResultPath = append(.currentResultPath, .FieldName)
	return 
}

func ( connectionVisitor) ( int) resultVisitor {
	.currentResultPath = append(.currentResultPath, fmt.Sprintf("[%d]", ))
	return 
}

func ( connectionVisitor) ( result) resultVisitor {
	// Already failed. Stop looking.
	if *.err != nil {
		return nil
	}

	 := strings.Join(.currentResultPath, ".")

	switch r := .(type) {

	case resultSingle:
		 := key{name: .Name, t: .Type}

		if  := .checkKey(, );  != nil {
			*.err = 
			return nil
		}
		for ,  := range .As {
			 := key{name: .Name, t: }
			if  := .checkKey(, );  != nil {
				*.err = 
				return nil
			}
		}

	case resultGrouped:
		// we don't really care about the path for this since conflicts are
		// okay for group results. We'll track it for the sake of having a
		// value there.
		 := key{group: .Group, t: .Type}
		.keyPaths[] = 
		for ,  := range .As {
			 := key{group: .Group, t: }
			.keyPaths[] = 
		}
	}

	return 
}

func ( connectionVisitor) ( key,  string) error {
	defer func() { .keyPaths[] =  }()
	if ,  := .keyPaths[];  {
		return newErrInvalidInput(fmt.Sprintf("cannot provide %v from %v", , ),
			newErrInvalidInput(fmt.Sprintf("already provided by %v", ), nil))
	}
	if  := .s.providers[]; len() > 0 {
		 := make([]string, len())
		for ,  := range  {
			[] = fmt.Sprint(.Location())
		}

		return newErrInvalidInput(fmt.Sprintf("cannot provide %v from %v", , ),
			newErrInvalidInput(fmt.Sprintf("already provided by %v", strings.Join(, "; ")), nil))
	}
	return nil
}