package nix

import (
	"path"
	"slices"
)

// this is pretty close to zero copy for the test case
const collectFromDerivationsInitialCap = 1 << 17

// CollectFromDerivations returns a deduplicated slice of store paths collected from derivations.
func CollectFromDerivations(derivations DerivationMap) []string {
	var setpointSize, growToCap int
	collective := make([]string, 0, collectFromDerivationsInitialCap)
	for p, drv := range derivations {
		if drv == nil {
			// should be unreachable
			continue
		}

		// another exponentially growing buffer here because range over map is expensive
		setpointSize = len(collective) + len(drv.InputSources) +
			// only map keys
			len(drv.InputDerivations) +
			// only Output.Path
			len(drv.Outputs) +
			// self, builder
			2
		if setpointSize > cap(collective) {
			growToCap = cap(collective) << 1
		tryGrow:
			if setpointSize > growToCap {
				// unlikely to be reached
				growToCap <<= 1
				goto tryGrow
			}

			collective = slices.Grow(collective, growToCap-cap(collective))
		}

		collective = append(collective, drv.InputSources...)
		for s := range drv.InputDerivations {
			collective = append(collective, s)
		}
		for _, out := range drv.Outputs {
			collective = append(collective, out.Path)
		}
		collective = append(collective, p)
		if path.IsAbs(drv.Builder) { // so builtins don't get collected
			collective = append(collective, drv.Builder)
		}
	}

	slices.Sort(collective)
	return slices.Compact(collective)
}
