package nix

import (
	"context"
	"encoding/json"
	"iter"
)

type (
	// DerivationMap is the output of `nix derivation show`.
	DerivationMap map[string]*Derivation

	// Derivation is a description of a derivation.
	Derivation struct {
		// Args are arguments passed to Builder.
		Args []string `json:"args"`
		// Builder is the store path of the program that builds the [Derivation].
		Builder string `json:"builder"`
		// System is the value of pkgs.system during evaluation.
		System string `json:"system"`
		// Name is the user-facing name of a derivation, as seen in the nix store path suffix.
		Name string `json:"name"`

		Environment      json.RawMessage     `json:"env"`
		InputDerivations InputDerivationsMap `json:"inputDrvs"`
		InputSources     []string            `json:"inputSrcs"`
		Outputs          OutputsMap          `json:"outputs"`
	}

	// OutputsMap is an output name to [Output] map.
	OutputsMap map[string]Output

	// Output is an output of a [Derivation].
	Output struct {
		Path string `json:"path"`
	}

	// InputDerivationsMap is a store path to metadata map.
	InputDerivationsMap map[string]InputDerivation

	// InputDerivation contains input derivation metadata.
	InputDerivation struct {
		DynamicOutputs json.RawMessage `json:"dynamicOutputs"`
		Outputs        []string        `json:"outputs"`
	}
)

// DerivationShow returns a [DerivationMap] describing all entries yielded by installables.
func DerivationShow(ctx Context, installables iter.Seq[string]) (DerivationMap, error) {
	c, cancel := context.WithCancel(ctx.Unwrap())
	defer cancel()
	_, stderr := ctx.Streams()
	var decoder *json.Decoder

	cmd := ctx.Nix(c, CommandDerivation, CommandDerivationShow,
		FlagStdin)
	if r, err := cmd.StdoutPipe(); err != nil {
		return nil, err
	} else {
		decoder = json.NewDecoder(r)
	}
	if stderr != nil {
		cmd.Stderr = stderr
	}

	var v DerivationMap
	_, err := ctx.WriteStdin(cmd, installables, func() error { return decoder.Decode(&v) })
	return v, err
}
