package service

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"strings"

	"github.com/pkg/errors"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/tidbcloud/cluster-agent/v1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"k8s.io/utils/ptr"
)

type ExecuteCmdArgs struct {
	Command string `json:"command" form:"command"`
}

type ExecuteCmdResult struct {
	Stdout string `json:"stdout"`
	Stderr string `json:"stderr"`
}

func (s *AgentService) ExecuteCommand(ctx context.Context, req *apiv1.ExecuteCommandReq) (*apiv1.ExecuteCommandResp, error) {
	resp := &apiv1.ExecuteCommandResp{}
	var err error
	resp.StdoutMsg, resp.StderrMsg, resp.ExitCode, err = execute(ctx, req.Command)

	return resp, err
}

func execute(ctx context.Context, command string) (stdoutMsg, stderrMsg string, exitCode *int32, _ error) {
	logger := tilog.WithContextS(ctx)
	cmdName, args, err := buildCommand(command)
	if err != nil {
		return "", "", nil, fmt.Errorf("build command: %w", err)
	}

	//nolint:gosec // Command execution is intended based on validated input
	cmd := exec.Command(cmdName, args...)
	var stdoutBuf, stderrBuf bytes.Buffer
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf

	if err := cmd.Run(); err != nil {
		logger.Infof("run command return err: %s", err)

		var exitError *exec.ExitError
		if errors.As(err, &exitError) {
			return stdoutBuf.String(), stderrBuf.String(), ptr.To(int32(exitError.ExitCode())), nil
		}
		return "", "", nil, fmt.Errorf("run command: %w", err)
	}

	return stdoutBuf.String(), stderrBuf.String(), ptr.To(int32(0)), nil
}

func buildCommand(command string) (cmdName string, args []string, _ error) {
	input := strings.TrimSpace(command)
	splits := strings.Split(input, " ")
	ctlCommand := splits[0]

	switch ctlCommand {
	case "pd-ctl", "cse-ctl", "echo":
		cmdName = "sh"
		args = []string{
			"-c",
			command,
		}
		return cmdName, args, nil
	default:
		return "", nil, fmt.Errorf("unsupported command %s", command)
	}
}
