package transformers

import (
	"os"

	"github.com/johnkerl/miller/v6/pkg/cli"
	"github.com/johnkerl/miller/v6/pkg/types"
)

// RecordTransformer is the interface satisfied by all transformers, i.e.,
// Miller verbs. See stream.go for context on the channels, as well as for
// context on end-of-record-stream signaling.
//
// A non-nil error from Transform means a mid-stream failure (e.g. a DSL
// runtime error, or a tee/split output file that can't be written). The
// chain driver forwards any already-produced output plus an end-of-stream
// marker downstream so the record-writer drains and finishes, then surfaces
// the error up through stream.Stream to the entrypoint layer (see
// plans/exit.md). Transformers must not call os.Exit.
type RecordTransformer interface {
	Transform(
		inrecAndContext *types.RecordAndContext,
		outputRecordsAndContexts *[]*types.RecordAndContext, // list of *types.RecordAndContext
		inputDownstreamDoneChannel <-chan bool,
		outputDownstreamDoneChannel chan<- bool,
	) error
}

// StreamingProducer is implemented by transformers that generate their own
// output independent of (and potentially unbounded relative to) their input
// -- currently only seqgen. Ordinary Transform implementations accumulate
// output in memory and hand it back to the caller for a single channel-send;
// that works because they're invoked once per input record, with the reader
// interleaving sends across many batches. seqgen has no such interleaving --
// with IgnoresInput it's invoked exactly once, with nothing to stop it from
// building its entire output in memory before anything downstream (e.g. mlr
// head) ever runs. ProduceStream instead writes directly and repeatedly to
// outputRecordChannel in bounded batches, checking inputDownstreamDoneChannel
// between batches, mirroring pseudo_reader_gen.go. See ChainTransformer.
type StreamingProducer interface {
	ProduceStream(
		outputRecordChannel chan<- []*types.RecordAndContext,
		inputDownstreamDoneChannel <-chan bool,
		outputDownstreamDoneChannel chan<- bool,
	)
}

type RecordTransformerFunc func(
	inrecAndContext *types.RecordAndContext,
	outputRecordsAndContexts *[]*types.RecordAndContext, // list of *types.RecordAndContext
	inputDownstreamDoneChannel <-chan bool,
	outputDownstreamDoneChannel chan<- bool,
) error

// Used within some verbs
type RecordTransformerHelperFunc func(
	inrecAndContext *types.RecordAndContext,
	outputRecordsAndContexts *[]*types.RecordAndContext, // list of *types.RecordAndContext
)

type TransformerUsageFunc func(
	ostream *os.File,
)

// TransformerParseCLIFunc parses verb options from the CLI. Returns (nil, nil) on
// success for pass one (doConstruct=false). Returns (transformer, nil) on success
// for pass two (doConstruct=true). Returns (nil, cli.ErrHelpRequested) when -h/--help
// was used (caller should exit 0; usage already printed). Returns (nil, err) on
// parse or constructor failure (caller should print and exit 1).
type TransformerParseCLIFunc func(
	pargi *int,
	argc int,
	args []string,
	mainOptions *cli.TOptions,
	doConstruct bool, // false for first pass of CLI-parse, true for second pass
) (RecordTransformer, error)

// OptionSpec describes one verb option in a machine-readable form. It is the
// building block for the structured Tier-2 catalog (PR3 of the AI-friendly
// roadmap). Verbs migrate to this incrementally; verbs with Options == nil
// fall back to the prose UsageText in the JSON catalog.
//
// Type is one of: bool, string, int, float, csv-list, regex, filename,
// format, enum.  For Type=="enum", Values contains every valid choice.
// Arg is the placeholder shown in usage text, e.g. "{n}" or "{a,b,c}";
// it is empty for bool flags.  Repeatable marks flags that may appear
// multiple times on the command line.  Aliases lists alternate spellings
// of Flag (e.g. "--sorted-input" for "-s").
type OptionSpec struct {
	Flag       string   `json:"flag"`
	Aliases    []string `json:"aliases,omitempty"`
	Arg        string   `json:"arg,omitempty"`
	Type       string   `json:"type"`
	Desc       string   `json:"desc"`
	Repeatable bool     `json:"repeatable,omitempty"`
	Values     []string `json:"values,omitempty"`
}

type TransformerSetup struct {
	Verb         string
	UsageFunc    TransformerUsageFunc
	ParseCLIFunc TransformerParseCLIFunc

	// For seqgen only -- all other transformers process records sourced by the
	// record-reader.  The seqgen verb, by contrast, is a record-source of its
	// own. (The seqgen verb probably should have been designed as a zero-file
	// record "reader" object, rather than a verb, alas.)
	IgnoresInput bool

	// Options is the structured option list for this verb. nil means the verb
	// has not yet been migrated to Tier-2; agents fall back to UsageText.
	// An explicitly empty slice means the verb accepts no options.
	Options []OptionSpec
}

// HandleDefaultDownstreamDone is a utility function for most verbs other than
// head, tee, and seqgen to use for passing downstream-done flags back
// upstream.  See ChainTransformer for context.
func HandleDefaultDownstreamDone(
	inputDownstreamDoneChannel <-chan bool,
	outputDownstreamDoneChannel chan<- bool,
) {
	select {
	case b := <-inputDownstreamDoneChannel:
		outputDownstreamDoneChannel <- b
	default:
	}
}
