package host

import (
	"bytes"
	"io"
	"os"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/test-infra/caselib/pkg/command"
	"github.com/pingcap/test-infra/caselib/pkg/utils"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

var logger *zap.Logger

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

type Host struct {
	resource.WorkloadNode
}

func (h *Host) ExecCmd(cmd string, timeout string) (*command.ExecOutput, error) {
	elapsed := utils.Try(time.ParseDuration(timeout)).(time.Duration)
	done := make(chan *command.ExecOutput, 1)
	errChan := make(chan error, 1)
	logger.Info("Execute command", zap.String("command", cmd), zap.String("timeout", timeout), zap.String("resource name", h.Handle().Name))
	go func() {
		var stdout, stderr bytes.Buffer
		start := time.Now()
		remote, _ := h.ExecAsync(resource.NopCloser(io.MultiWriter(os.Stdout, &stdout)), resource.NopCloser(io.MultiWriter(os.Stdout, &stderr)), resource.WorkloadNodeExecOptions{Command: cmd})
		waitErr := remote.Wait()
		if waitErr.ExitCode != 0 {
			logger.Info("Execute command error", zap.String("command", cmd), zap.Int("exit code", waitErr.ExitCode), zap.String("stdout", stdout.String()), zap.String("stderr", stderr.String()), zap.Error(waitErr.Err))
		}
		end := time.Now()
		duration := end.Sub(start)
		logger.Info("Execute command finished", zap.String("command", cmd), zap.Duration("execute duration", duration))
		done <- &command.ExecOutput{Stdout: stdout.String(), Stderr: stderr.String(), ExitCode: waitErr.ExitCode}
	}()

	select {
	case output := <-done:
		return output, nil
	case err := <-errChan:
		logger.Error("Execute command error", zap.Error(err))
		return nil, errors.Trace(err)
	case <-time.After(elapsed):
		return nil, errors.New("Exec command timeout")
	}
}
