// Package shellx contains utilities to run external commands.
package shellx

import (
	"errors"
	"os"
	"strings"

	"golang.org/x/sys/execabs"

	"github.com/apex/log"
	"github.com/google/shlex"
	"github.com/ooni/probe-cli/v3/internal/engine/model"
)

type runconfig struct {
	args     []string
	loginfof func(format string, v ...interface{})
	name     string
	stdout   *os.File
	stderr   *os.File
}

func run(config runconfig) error {
	config.loginfof("exec: %s %s", config.name, strings.Join(config.args, " "))
	cmd := execabs.Command(config.name, config.args...)
	cmd.Stdout = config.stdout
	cmd.Stderr = config.stderr
	err := cmd.Run()
	config.loginfof("exec result: %+v", err)
	return err
}

// Run executes the specified command with the specified args
func Run(name string, arg ...string) error {
	return run(runconfig{
		args:     arg,
		loginfof: log.Log.Infof,
		name:     name,
		stdout:   os.Stdout,
		stderr:   os.Stderr,
	})
}

// RunQuiet is like Run but it does not emit any output.
func RunQuiet(name string, arg ...string) error {
	return run(runconfig{
		args:     arg,
		loginfof: model.DiscardLogger.Infof,
		name:     name,
		stdout:   nil,
		stderr:   nil,
	})
}

// RunCommandline is like Run but its only argument is a command
// line that will be splitted using the google/shlex package
func RunCommandline(cmdline string) error {
	args, err := shlex.Split(cmdline)
	if err != nil {
		return err
	}
	if len(args) < 1 {
		return errors.New("shellx: no command to execute")
	}
	return Run(args[0], args[1:]...)
}