package execution

import "github.com/tomwright/dasel/v3/model"

// ExecuteOptionFn is a function that can be used to set options on the execution of the selector.
type ExecuteOptionFn func(*Options)

// Options contains the options for the execution of the selector.
type Options struct {
	Funcs    FuncCollection
	Vars     map[string]*model.Value
	Unstable bool
}

// NewOptions creates a new Options struct with the given options.
func NewOptions(opts ...ExecuteOptionFn) *Options {
	o := &Options{
		Funcs: DefaultFuncCollection,
		Vars:  map[string]*model.Value{},
	}
	for _, opt := range opts {
		if opt == nil {
			continue
		}
		opt(o)
	}
	return o
}

// WithFuncs sets the functions that can be used in the selector.
func WithFuncs(fc FuncCollection) ExecuteOptionFn {
	return func(o *Options) {
		o.Funcs = fc
	}
}

// WithVariable sets a variable for use in the selector.
func WithVariable(key string, val *model.Value) ExecuteOptionFn {
	return func(o *Options) {
		o.Vars[key] = val
	}
}

// WithUnstable allows access to potentially unstable features.
func WithUnstable() ExecuteOptionFn {
	return func(o *Options) {
		o.Unstable = true
	}
}

// WithoutUnstable disallows access to potentially unstable features.
func WithoutUnstable() ExecuteOptionFn {
	return func(o *Options) {
		o.Unstable = false
	}
}

// withKeyVar sets the $key variable to the given value and returns a function
// that restores the previous value (or deletes it if it didn't exist).
func withKeyVar(options *Options, key *model.Value) func() {
	prev, had := options.Vars["key"]
	options.Vars["key"] = key
	return func() {
		if had {
			options.Vars["key"] = prev
		} else {
			delete(options.Vars, "key")
		}
	}
}
