package solve

import (
	
	
	

	
	
	
)

// ResultKind classifies one entry of a provider's result tuple.
type ResultKind int

// The result kinds for classifying entries of a provider’s result tuple.
const (
	ResultKindValue   ResultKind = iota // an ordinary value output
	ResultKindCleanup                   // a bare func() / func() error teardown hook
	ResultKindError                     // the predeclared error channel
)

// ResultSlot is one classified entry of a provider's result tuple.
type ResultSlot struct {
	Kind     ResultKind
	Typ      types.Type // ResultKindValue: the value type; otherwise nil
	Failable bool       // ResultKindCleanup: true if func() error
}

// InputSlot is one consumed type, in call order.
type InputSlot struct {
	typ types.Type

	// Name is the source parameter/field name, a hint for naming an injector
	// input. For a struct-type provider it is the exported field name, which emit
	// renders into the composite literal.
	Name string

	// flexRecv marks the implicit receiver of a struct-field provider, whose
	// value-vs-pointer form is chosen during solving from the rest of the set.
	flexRecv bool
	flexBase types.Type // the struct type (value form) for a flexible receiver: a *Named, or a *Alias when the field is on an alias to an anonymous struct
}

// Instance is a concrete (fully instantiated) provider ready for wiring: pure
// data describing what to call with what. emit turns it into Go source. For a
// non-generic provider there is exactly one; for a template there is one per
// pinning.
type Instance struct {
	Prov    *discover.Provider // the source provider this instantiates
	Targs   []types.Type       // type arguments, aligned with Prov.Tparams; nil if concrete
	Inputs  []InputSlot        // consumed types, in call order
	Results []ResultSlot       // classified results, in result order

	pos token.Position
}

// valueOuts returns the value-output types of the instance, in result order.
func ( *Instance) () []types.Type {
	var  []types.Type
	for ,  := range .Results {
		if .Kind == ResultKindValue {
			 = append(, .Typ)
		}
	}
	return 
}

// InputTypes returns the instance's input types, in call order (for the report).
func ( *Instance) () []types.Type {
	var  []types.Type
	for ,  := range .Inputs {
		 = append(, .typ)
	}
	return 
}

// instantiate produces the concrete instance of p for the given type arguments
// (empty for a concrete provider): it classifies the provider's inputs and
// results, with no rendering: emit decides how each kind is written. It returns
// a located error (*diag.Error) for user-visible problems (e.g. multiple error
// results), and a non-nil miss error (the type-checker's own *types.ArgumentError)
// when the instantiation is infeasible because targs violate a type-parameter
// constraint, which the solver treats as a near-miss non-match. Both nil means a
// matched instance. The per-kind helpers panic on a post-instantiation structural
// failure (a method/field that vanished), which is unreachable for valid input.
func ( *discover.Provider,  *types.Context,  []types.Type) ( *Instance,  *diag.Error,  error) {
	 = &Instance{Prov: , Targs: , pos: .Pos}
	switch .Kind {
	case discover.KindFunc:
		,  := instSignature(, .Fn.Type().(*types.Signature), )
		if  != nil {
			return nil, nil, 
		}
		classifyParams(, , 0)
		if  := classifyResults(, , );  != nil {
			return nil, , nil
		}
	case discover.KindMethod:
		, ,  := methodSignature(, , )
		if  != nil {
			return nil, nil, 
		}
		// receiver is the first input.
		.Inputs = append(.Inputs, InputSlot{typ: })
		classifyParams(, , 0)
		if  := classifyResults(, , );  != nil {
			return nil, , nil
		}
	case discover.KindSymbol:
		.Results = []ResultSlot{{Kind: ResultKindValue, Typ: .Sym.Type()}}
	case discover.KindConvert:
		.Inputs = []InputSlot{{typ: .ConvertFrom}}
		.Results = []ResultSlot{{Kind: ResultKindValue, Typ: .ConvertTo}}
	case discover.KindField:
		, ,  := fieldTypes(, , )
		if  != nil {
			return nil, nil, 
		}
		.Inputs = []InputSlot{{flexRecv: true, flexBase: , typ: }}
		.Results = []ResultSlot{{Kind: ResultKindValue, Typ: }}
	case discover.KindStruct:
		, ,  := structFields(, , )
		if  != nil {
			return nil, nil, 
		}
		for ,  := range  {
			.Inputs = append(.Inputs, InputSlot{typ: .Type(), Name: .Name()})
		}
		.Results = []ResultSlot{{Kind: ResultKindValue, Typ: }}
	default:
		panic(fmt.Sprintf("plumb: unhandled provider kind %d", .Kind))
	}
	return , nil, nil
}

// instSignature instantiates a generic signature, or returns it unchanged when
// there are no type args. A non-nil error is the constraint near-miss reason.
func ( *types.Context,  *types.Signature,  []types.Type) (*types.Signature, error) {
	if len() == 0 {
		return , nil
	}
	,  := types.Instantiate(, , , true)
	if  != nil {
		return nil, 
	}
	return .(*types.Signature), nil
}

// methodSignature returns the receiver input type and the (possibly
// instantiated) signature of a method provider, whether the receiver is a
// concrete named type or an interface. For a concrete receiver the input is the
// method's own (value- or pointer-) receiver type; for an interface it is the
// interface named type itself, instantiated at targs. A non-nil error is the
// constraint near-miss reason.
func ( *discover.Provider,  *types.Context,  []types.Type) ( types.Type,  *types.Signature,  error) {
	 := .Owner
	if len() > 0 {
		,  := types.Instantiate(, gotypes.GenericOrigin(.Owner), , true)
		if  != nil {
			return nil, nil, 
		}
		 = 
	}
	if ,  := .Underlying().(*types.Interface);  {
		for  := range .Methods() {
			if .Name() == .Fn.Name() {
				return , .Type().(*types.Signature), nil
			}
		}
		panic(fmt.Sprintf("plumb: method %s of provider %s vanished after instantiation", .Fn.Name(), .Name))
	}
	 := lookupMethod(.(*types.Named), .Fn.Name())
	if  == nil {
		panic(fmt.Sprintf("plumb: method %s of provider %s vanished after instantiation", .Fn.Name(), .Name))
	}
	 := .Type().(*types.Signature)
	return .Recv().Type(), , nil
}

// fieldTypes returns the field's type and the (possibly instantiated) receiver
// type for a field provider: a *types.Named, or a *types.Alias when the field is
// on an alias to an anonymous struct. A non-nil error is the constraint near-miss
// reason.
func ( *discover.Provider,  *types.Context,  []types.Type) (types.Type, types.Type, error) {
	 := .Owner
	if len() > 0 {
		,  := types.Instantiate(, gotypes.GenericOrigin(.Owner), , true)
		if  != nil {
			return nil, nil, 
		}
		 = 
	}
	,  := .Underlying().(*types.Struct)
	if ! {
		panic(fmt.Sprintf("plumb: receiver of field provider %s is not a struct after instantiation", .Name))
	}
	for  := range .Fields() {
		if .Name() == .Sym.Name() {
			return .Type(), , nil
		}
	}
	panic(fmt.Sprintf("plumb: field %s of provider %s vanished after instantiation", .Sym.Name(), .Name))
}

// structFields returns the declared type to render (the directive's named or
// alias type, instantiated at targs) and its exported struct fields. The alias is
// kept as written so the composite literal names it (and an exported alias to an
// unexported struct stays reachable across packages); the fields come from its
// flattened underlying struct. A non-nil error is the constraint near-miss reason.
func ( *discover.Provider,  *types.Context,  []types.Type) ( types.Type,  []*types.Var,  error) {
	 = .Declared
	if len() > 0 {
		,  := types.Instantiate(, gotypes.GenericOrigin(.Declared), , true)
		if  != nil {
			return nil, nil, 
		}
		 = 
	}
	,  := .Underlying().(*types.Struct)
	if ! {
		panic(fmt.Sprintf("plumb: struct-type provider %s is not a struct after instantiation", .Name))
	}
	for  := range .Fields() {
		if .Exported() {
			 = append(, )
		}
	}
	return , , nil
}

// classifyParams appends the signature parameters (starting at index start) as
// inputs. A variadic parameter needs nothing special here: the tuple already
// types it as its slice ([]T), and the call-site spread (x...) is emit's job.
func ( *Instance,  *types.Signature,  int) {
	 := .Params()
	for  := ;  < .Len(); ++ {
		 := .At()
		.Inputs = append(.Inputs, InputSlot{typ: .Type(), Name: .Name()})
	}
}

// classifyResults classifies the signature results into value/cleanup/error
// slots, enforcing the at-most-one-error rule.
func ( *discover.Provider,  *Instance,  *types.Signature) *diag.Error {
	 := .Results()
	 := 0
	for  := range .Variables() {
		 := .Type()
		switch {
		case gotypes.IsErrorType():
			++
			if  > 1 {
				return diag.Errorf(.Pos, diag.ErrMultipleErrors, "provider %s returns more than one error result; at most one is supported", .Name)
			}
			.Results = append(.Results, ResultSlot{Kind: ResultKindError})
		case gotypes.IsBareCleanup():
			.Results = append(.Results, ResultSlot{Kind: ResultKindCleanup, Failable: false})
		case gotypes.IsFailableCleanup():
			.Results = append(.Results, ResultSlot{Kind: ResultKindCleanup, Failable: true})
		default:
			.Results = append(.Results, ResultSlot{Kind: ResultKindValue, Typ: })
		}
	}
	return nil
}

func ( *types.Named,  string) *types.Func {
	for  := range .Methods() {
		if .Name() ==  {
			return 
		}
	}
	return nil
}