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

	
	
)

// Error is an interface implemented by all Dig errors.
//
// Use this interface, in conjunction with [RootCause], in order to
// determine if errors you encounter come from Dig, or if they come
// from provided constructors or invoked functions. See [RootCause]
// for more info.
type Error interface {
	error

	// Writes the message or context for this error in the chain.
	//
	// Note: the Error interface must always have a private function
	// such as this one in order to maintain properly sealed.
	//
	// verb is either %v or %+v.
	writeMessage(w io.Writer, v string)
}

// a digError is a dig.Error with additional functionality for
// internal use - namely the ability to be formatted.
type digError interface {
	Error
	fmt.Formatter
}

// A PanicError occurs when a panic occurs while running functions given to the container
// with the [RecoverFromPanic] option being set. It contains the panic message from the
// original panic. A PanicError does not wrap other errors, and it does not implement
// dig.Error, meaning it will be returned from [RootCause]. With the [RecoverFromPanic]
// option set, a panic can be distinguished from dig errors and errors from provided/
// invoked/decorated functions like so:
//
//	rootCause := dig.RootCause(err)
//
//	var pe dig.PanicError
//	var de dig.Error
//	if errors.As(rootCause, &pe) {
//		// This is caused by a panic
//	} else if errors.As(err, &de) {
//		// This is a dig error
//	} else {
//		// This is an error from one of my provided/invoked functions or decorators
//	}
//
// Or, if only interested in distinguishing panics from errors:
//
//	var pe dig.PanicError
//	if errors.As(err, &pe) {
//		// This is caused by a panic
//	} else {
//		// This is an error
//	}
type PanicError struct {
	// The function the panic occurred at
	fn *digreflect.Func

	// The panic that was returned from recover()
	Panic any
}

// Format will format the PanicError, expanding the corresponding function if in +v mode.
func ( PanicError) ( fmt.State,  rune) {
	if .Flag('+') &&  == 'v' {
		fmt.Fprintf(, "panic: %q in func: %+v", .Panic, .fn)
	} else {
		fmt.Fprintf(, "panic: %q in func: %v", .Panic, .fn)
	}
}

func ( PanicError) () string {
	return fmt.Sprint()
}

// formatError will call a dig.Error's writeMessage() method to print the error message
// and then will automatically attempt to print errors wrapped underneath (which can create
// a recursive effect if the wrapped error's Format() method then points back to this function).
func formatError( digError,  fmt.State,  rune) {
	 := .Flag('+') &&  == 'v'
	 := "%v"
	if  {
		 = "%+v"
	}

	// "context: " or "context:\n"
	.writeMessage(, )

	// Will route back to this function recursively if next error
	// is also wrapped and points back here
	 := errors.Unwrap()
	if  == nil {
		return
	}
	io.WriteString(, ":")
	if  {
		io.WriteString(, "\n")
	} else {
		io.WriteString(, " ")
	}
	fmt.Fprintf(, , )
}

// RootCause returns the first non-dig.Error in a chain of wrapped
// errors, if there is one. Otherwise, RootCause returns the error
// on the bottom of the chain of wrapped errors.
//
// Use this function and errors.As to differentiate between Dig errors
// and errors thrown by provided constructors or invoked functions:
//
//	rootCause := dig.RootCause(err)
//	var de dig.Error
//	if errors.As(rootCause, &de) {
//	    // Is a Dig error
//	} else {
//	    // Is an error thrown by one of my provided/invoked/decorated functions
//	}
//
// See [PanicError] for an example showing how to additionally detect
// and handle panics in provided/invoked/decorated functions.
func ( error) error {
	var  Error
	// Dig down to first non dig.Error, or bottom of chain
	for ; errors.As(, &);  = errors.Unwrap() {
	}

	if  == nil {
		return 
	}

	return 
}

// errInvalidInput is returned whenever the user provides bad input when
// interacting with the container. May optionally have a more detailed
// error wrapped underneath.
type errInvalidInput struct {
	Message string
	Cause   error
}

var _ digError = errInvalidInput{}

// newErrInvalidInput creates a new errInvalidInput, wrapping the given
// other error that caused this error. If there is no underlying cause,
// pass in nil. This will cause all attempts to unwrap this error to return
// nil, replicating errors.Unwrap's behavior when passed an error without
// an Unwrap() method.
func newErrInvalidInput( string,  error) errInvalidInput {
	return errInvalidInput{, }
}

func ( errInvalidInput) () string { return fmt.Sprint() }

func ( errInvalidInput) () error { return .Cause }

func ( errInvalidInput) ( io.Writer,  string) {
	fmt.Fprint(, .Message)
}

func ( errInvalidInput) ( fmt.State,  rune) {
	formatError(, , )
}

// errProvide is returned when a constructor could not be Provided into the
// container.
type errProvide struct {
	Func   *digreflect.Func
	Reason error
}

var _ digError = errProvide{}

func ( errProvide) () string { return fmt.Sprint() }

func ( errProvide) () error { return .Reason }

func ( errProvide) ( io.Writer,  string) {
	fmt.Fprintf(, "cannot provide function "+, .Func)
}

func ( errProvide) ( fmt.State,  rune) {
	formatError(, , )
}

// errConstructorFailed is returned when a user-provided constructor failed
// with a non-nil error.
type errConstructorFailed struct {
	Func   *digreflect.Func
	Reason error
}

var _ digError = errConstructorFailed{}

func ( errConstructorFailed) () string { return fmt.Sprint() }

func ( errConstructorFailed) () error { return .Reason }

func ( errConstructorFailed) ( io.Writer,  string) {
	fmt.Fprintf(, "received non-nil error from function "+, .Func)
}

func ( errConstructorFailed) ( fmt.State,  rune) {
	formatError(, , )
}

// errArgumentsFailed is returned when a function could not be run because one
// of its dependencies failed to build for any reason.
type errArgumentsFailed struct {
	Func   *digreflect.Func
	Reason error
}

var _ digError = errArgumentsFailed{}

func ( errArgumentsFailed) () string { return fmt.Sprint() }

func ( errArgumentsFailed) () error { return .Reason }

func ( errArgumentsFailed) ( io.Writer,  string) {
	fmt.Fprintf(, "could not build arguments for function "+, .Func)
}

func ( errArgumentsFailed) ( fmt.State,  rune) {
	formatError(, , )
}

// errMissingDependencies is returned when the dependencies of a function are
// not available in the container.
type errMissingDependencies struct {
	Func   *digreflect.Func
	Reason error
}

var _ digError = errMissingDependencies{}

func ( errMissingDependencies) () string { return fmt.Sprint() }

func ( errMissingDependencies) () error { return .Reason }

func ( errMissingDependencies) ( io.Writer,  string) {
	fmt.Fprintf(, "missing dependencies for function "+, .Func)
}

func ( errMissingDependencies) ( fmt.State,  rune) {
	formatError(, , )
}

// errParamSingleFailed is returned when a paramSingle could not be built.
type errParamSingleFailed struct {
	Key    key
	Reason error
	CtorID dot.CtorID
}

var _ digError = errParamSingleFailed{}

func ( errParamSingleFailed) () string { return fmt.Sprint() }

func ( errParamSingleFailed) () error { return .Reason }

func ( errParamSingleFailed) ( io.Writer,  string) {
	fmt.Fprintf(, "failed to build %v", .Key)
}

func ( errParamSingleFailed) ( fmt.State,  rune) {
	formatError(, , )
}

func ( errParamSingleFailed) ( *dot.Graph) {
	 := &dot.Result{
		Node: &dot.Node{
			Name:  .Key.name,
			Group: .Key.group,
			Type:  .Key.t,
		},
	}
	.FailNodes([]*dot.Result{}, .CtorID)
}

// errParamGroupFailed is returned when a value group cannot be built because
// any of the values in the group failed to build.
type errParamGroupFailed struct {
	Key    key
	Reason error
	CtorID dot.CtorID
}

var _ digError = errParamGroupFailed{}

func ( errParamGroupFailed) () string { return fmt.Sprint() }

func ( errParamGroupFailed) () error { return .Reason }

func ( errParamGroupFailed) ( io.Writer,  string) {
	fmt.Fprintf(, "could not build value group %v", .Key)
}

func ( errParamGroupFailed) ( fmt.State,  rune) {
	formatError(, , )
}

func ( errParamGroupFailed) ( *dot.Graph) {
	.FailGroupNodes(.Key.group, .Key.t, .CtorID)
}

// missingType holds information about a type that was missing in the
// container.
type missingType struct {
	Key key // item that was missing

	// If non-empty, we will include suggestions for what the user may have
	// meant.
	suggestions []key
}

// Format prints a string representation of missingType.
//
// With %v, it prints a short representation ideal for an itemized list.
//
//	io.Writer
//	io.Writer: did you mean *bytes.Buffer?
//	io.Writer: did you mean *bytes.Buffer, or *os.File?
//
// With %+v, it prints a longer representation ideal for standalone output.
//
//	io.Writer: did you mean to Provide it?
//	io.Writer: did you mean to use *bytes.Buffer?
//	io.Writer: did you mean to use one of *bytes.Buffer, or *os.File?
func ( missingType) ( fmt.State,  rune) {
	 := .Flag('+') &&  == 'v'

	fmt.Fprint(, .Key)
	switch len(.suggestions) {
	case 0:
		if  {
			io.WriteString(, " (did you mean to Provide it?)")
		}
	case 1:
		 := .suggestions[0]
		if  {
			fmt.Fprintf(, " (did you mean to use %v?)", )
		} else {
			fmt.Fprintf(, " (did you mean %v?)", )
		}
	default:
		if  {
			io.WriteString(, " (did you mean to use one of ")
		} else {
			io.WriteString(, " (did you mean ")
		}

		 := len(.suggestions) - 1
		for ,  := range .suggestions {
			if  > 0 {
				io.WriteString(, ", ")
				if  ==  {
					io.WriteString(, "or ")
				}
			}
			fmt.Fprint(, )
		}
		io.WriteString(, "?)")
	}
}

// errMissingType is returned when one or more values that were expected in
// the container were not available.
//
// Multiple instances of this error may be merged together by appending them.
type errMissingTypes []missingType // inv: len > 0

var _ digError = errMissingTypes(nil)

func newErrMissingTypes( containerStore,  key) errMissingTypes {
	// Possible types we will look for in the container. We will always look
	// for pointers to the requested type and some extras on a per-Kind basis.
	 := []reflect.Type{reflect.PointerTo(.t)}

	if .t.Kind() == reflect.Ptr {
		// The user requested a pointer but maybe we have a value.
		 = append(, .t.Elem())
	}

	if .t.Kind() == reflect.Slice {
		// Maybe the user meant a slice of pointers while we have the slice of elements
		 = append(, reflect.SliceOf(reflect.PointerTo(.t.Elem())))

		// Maybe the user meant a slice of elements while we have the slice of pointers
		 := .t.Elem()
		if .Kind() == reflect.Ptr {
			 = append(, reflect.SliceOf(.Elem()))
		}
	}

	if .t.Kind() == reflect.Array {
		// Maybe the user meant an array of pointers while we have the array of elements
		 = append(, reflect.ArrayOf(.t.Len(), reflect.PointerTo(.t.Elem())))

		// Maybe the user meant an array of elements while we have the array of pointers
		 := .t.Elem()
		if .Kind() == reflect.Ptr {
			 = append(, reflect.ArrayOf(.t.Len(), .Elem()))
		}
	}

	 := .knownTypes()
	if .t.Kind() == reflect.Interface {
		// Maybe we have an implementation of the interface.
		for ,  := range  {
			if .Implements(.t) {
				 = append(, )
			}
		}
	} else {
		// Maybe we have an interface that this type implements.
		for ,  := range  {
			if .Kind() == reflect.Interface {
				if .t.Implements() {
					 = append(, )
				}
			}
		}
	}

	// range through c.providers is non-deterministic. Let's sort the list of
	// suggestions.
	sort.Sort(byTypeName())

	 := missingType{Key: }
	for ,  := range  {
		if len(.getValueProviders(.name, )) > 0 {
			.t = 
			.suggestions = append(.suggestions, )
		}
	}

	return errMissingTypes{}
}

func ( errMissingTypes) () string { return fmt.Sprint() }

func ( errMissingTypes) ( io.Writer,  string) {
	 :=  == "%+v"

	if len() == 1 {
		io.WriteString(, "missing type:")
	} else {
		io.WriteString(, "missing types:")
	}

	if ! {
		// With %v, we need a space between : since the error
		// won't be on a new line.
		io.WriteString(, " ")
	}

	for ,  := range  {
		if  {
			io.WriteString(, "\n\t- ")
		} else if  > 0 {
			io.WriteString(, "; ")
		}

		if  {
			fmt.Fprintf(, "%+v", )
		} else {
			fmt.Fprintf(, "%v", )
		}
	}
}

func ( errMissingTypes) ( fmt.State,  rune) {
	formatError(, , )
}

func ( errMissingTypes) ( *dot.Graph) {
	 := make([]*dot.Result, len())

	for ,  := range  {
		[] = &dot.Result{
			Node: &dot.Node{
				Name:  .Key.name,
				Group: .Key.group,
				Type:  .Key.t,
			},
		}
	}
	.AddMissingNodes()
}

type errVisualizer interface {
	updateGraph(*dot.Graph)
}