package solve

import (
	
	
	
	

	
	
)

// injectorInput is a deduplicated injector parameter: its type, the source-name
// hint and position of its earliest consumer (for ordering and naming).
type injectorInput struct {
	typ  types.Type
	pos  token.Position
	name string
}

// finalize resolves every input, classifies the signature, runs reachability
// and reserved-name checks, orders the instances, and builds the plan.
func ( *solver) () (*Plan, *diag.Error) {
	 := &Plan{
		Name:          .name,
		pos:           .setPos(),
		Args:          map[*Instance][]ArgRef{},
		ProviderCount: len(.provs),
	}

	var  gotypes.Set[types.Type]                  // value types some instance consumes
	var  gotypes.Map[types.Type, *injectorInput] // injector input type → its dedup record

	for ,  := range .instances {
		 := make([]ArgRef, len(.Inputs))
		for ,  := range .Inputs {
			,  := .resolveInput(, )
			if  != nil {
				return nil, 
			}
			[] = 
			if .isParam {
				if ,  := .At(.SrcType); ! {
					.Set(.SrcType, &injectorInput{typ: .SrcType, pos: .pos, name: .Name})
				} else if diag.CmpPos(.pos, .pos) < 0 {
					.pos = .pos
					.name = .Name
				}
			} else {
				.Add(.SrcType)
			}
		}
		.Args[] = 
	}

	// Value outputs: produced but not consumed.
	var  []types.Type
	for ,  := range .instances {
		for ,  := range .valueOuts() {
			if !.Contains() {
				 = append(, )
			}
		}
	}
	sortTypesByProducer(, &.supply)
	.Outputs = 

	// Inputs: deduped, ordered by earliest consumer then type.
	 := make([]*injectorInput, 0, .Len())
	for ,  := range .All {
		 = append(, )
	}
	slices.SortFunc(, func(,  *injectorInput) int {
		return cmp.Or(diag.CmpPos(.pos, .pos), gotypes.CmpType(.typ, .typ))
	})
	for ,  := range  {
		.Inputs = append(.Inputs, Input{Type: .typ, Name: .name})
	}

	// Cleanup / error aggregation.
	for ,  := range .instances {
		for ,  := range .Results {
			switch .Kind {
			case ResultKindCleanup:
				.AnyCleanup = true
				if .Failable {
					.CleanupFailable = true
				}
			case ResultKindError:
				.Fallible = true
			}
		}
	}

	// Lifted parameters, in deterministic header order.
	.Lifted = .orderedLifted()

	if  := .checkReachability();  != nil {
		return nil, 
	}
	if  := .checkReservedAndCollision();  != nil {
		return nil, 
	}

	,  := .topoOrder()
	if  != nil {
		return nil, 
	}
	.Order = 
	return , nil
}

// resolveInput maps one provider input to its source: an exact producer, a
// value/pointer-bridged producer, or an injector parameter.
func ( *solver) ( *Instance,  InputSlot) (ArgRef, *diag.Error) {
	 := .typ
	if  := .checkInputType(, );  != nil {
		return ArgRef{}, 
	}
	if ,  := .supply.At();  {
		return ArgRef{SrcType: , Coerce: CoerceNone}, nil
	}
	if ,  := gotypes.DualType();  {
		if ,  := .supply.At();  {
			return ArgRef{SrcType: , Coerce: bridgeDir()}, nil
		}
	}
	return ArgRef{isParam: true, SrcType: }, nil
}

func ( []types.Type,  *gotypes.Map[types.Type, *Instance]) {
	 := func( types.Type) *Instance {
		,  := .At()
		return 
	}
	slices.SortStableFunc(, func(,  types.Type) int {
		 := ()
		 := ()
		if  != nil &&  != nil {
			if  := diag.CmpPos(.pos, .pos);  != 0 {
				return 
			}
		}
		return gotypes.CmpType(, )
	})
}