package command

import (
	"fmt"
	"strings"

	"go.uber.org/zap"

	"github.com/pingcap/test-infra/caselib/pkg/utils"
)

var logger *zap.Logger

var _ ExecutableCmd = &ExecCmd{}

func init() {
	_, logger = utils.GetCtxAndLogger()
}

type ExecutableCmd interface {
	Compose() string
}

type ExecCmd struct {
	ParentCmd ExecutableCmd
	Command   string
	Options   []string
}

func (c *ExecCmd) Compose() string {
	cmd := c.Command
	flags := ""

	if len(c.Options) > 0 {
		flags = "\"" + strings.Join(c.Options, "\" \"") + "\""
	}
	cmd = strings.Join([]string{cmd, flags}, " ")

	if c.ParentCmd != nil {
		cmd = c.ParentCmd.Compose() + " " + cmd
	}
	return cmd
}

type ExecOutput struct {
	Stdout   string
	Stderr   string
	ExitCode int
	Err      error
}

func (e *ExecOutput) CheckErr() bool {
	if e.Stderr != "" || e.ExitCode != 0 {
		return true
	}
	if e.Err != nil {
		logger.Error("run error", zap.Error(e.Err))
		return true
	}
	return false
}

func (e *ExecOutput) String() string {
	return fmt.Sprintf("{stdout: %s, stderr: %s, ExitCode: %d", e.Stdout, e.Stderr, e.ExitCode)
}

// If used as the parent command, would inject the environment variables into the command.
type SetEnv map[string]map[string]string

func (s SetEnv) Compose() string {
	env, ok := s["env"]
	if ok {
		pairs := make([]string, 0, len(env))
		for k, v := range env {
			pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
		}
		return strings.Join(pairs, " ")
	} else {
		return ""
	}
}
