package types

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os/exec"

	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/schrodinger/util"
)

// ActionType is action type
type ActionType string

// ActionType
const (
	ActionSaveCfg    ActionType = "SAVECFG"
	ActionExecuteCmd ActionType = "EXECCMD"
)

// Action is action deamon can do as helper, such as save config or others
type Action struct {
	Type ActionType `json:"type"`
	Data []byte     `json:"data"`
}

// Do does action
func (a *Action) Do(ctx context.Context) error {
	switch a.Type {
	case ActionSaveCfg:
		return errors.Trace(util.RetryOnError(ctx, 5, a.saveManagerCfg))
	case ActionExecuteCmd:
		return errors.Trace(util.RetryOnError(ctx, 5, a.execCmd))
	}
	return nil
}

// ConfigData is config data
type ConfigData struct {
	// object addr
	Addr     string `json:"addr"`
	SavePath string `json:"save_path"`
}

func (a *Action) saveManagerCfg() error {
	cfg := &ConfigData{}
	err := json.Unmarshal(a.Data, cfg)
	if err != nil {
		return errors.Trace(err)
	}

	data, err := util.GetAndExtract(http.DefaultClient, cfg.Addr)
	if err != nil {
		return errors.Trace(err)
	}

	object := &Object{}
	err = json.Unmarshal(data, object)
	if err != nil {
		return errors.Trace(err)
	}

	return errors.Trace(ioutil.WriteFile(cfg.SavePath, object.Payload, 0644))
}

// ExecConfig is execute config
type ExecConfig struct {
	Command string `json:"command"`
}

func (a *Action) execCmd() error {
	cfg := &ExecConfig{}

	err := json.Unmarshal(a.Data, cfg)
	if err != nil {
		return errors.Trace(err)
	}
	cmd := exec.Command("/bin/sh", "-c", cfg.Command)
	stdoutStderr, err := cmd.CombinedOutput()

	log.Infof("exec %s stdout %s", cfg.Command, string(stdoutStderr))

	return errors.Trace(err)
}
