package host

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/caselib/pkg/command"
	"github.com/pingcap/test-infra/caselib/pkg/utils"
	"github.com/pingcap/test-infra/sdk/resource"
)

type LightningHost struct{ Host }

type ImportOpts struct {
	DataDir       string
	PdUrl         string
	Host          string
	Port          string
	User          string
	Pass          string
	Backend       string
	SortedKvDir   string
	Cfg           string
	TransferToCfg bool
}

func (i *ImportOpts) Get(field string) string {
	r := reflect.ValueOf(i)
	f := reflect.Indirect(r).FieldByName(field)
	return f.String()
}

var argMap = map[string]string{
	"DataDir":     "-d",
	"PdUrl":       "-pd-urls",
	"Host":        "-tidb-host",
	"Port":        "-tidb-port",
	"User":        "-tidb-user",
	"Pass":        "-tidb-password",
	"Backend":     "-backend",
	"SortedKvDir": "-sorted-kv-dir",
}

func (i *ImportOpts) Args() []string {
	args := make([]string, 16)
	index := 0
	for k, v := range argMap {
		args[index] = v
		args[index+1] = i.Get(k)
		index = index + 2
	}
	return args
}

func (i *ImportOpts) Config() string {
	cfg := `[mydumper]
data-source-dir = "%s"
[tidb]
host = "%s"
port = %s
user = "%s"
password = "%s"
pd-addr = "%s"
[tikv-importer]
backend = "%s"
sorted-kv-dir = "%s"
`
	return fmt.Sprintf(cfg, i.DataDir, i.Host, i.Port, i.User, i.Pass, i.PdUrl, i.Backend, i.SortedKvDir)
}

func (l *LightningHost) Lightning(args ...string) *command.LightningCmd {
	return &command.LightningCmd{ExecCmd: command.ExecCmd{Command: "/tidb-lightning", Options: args}}
}

func (l *LightningHost) TiUPLightning(version string, args ...string) *command.LightningCmd {
	var cmd string
	if version == "" {
		cmd = "tiup tidb-lightning"
	} else {
		cmd = "tiup tidb_lightning:" + version
	}
	return &command.LightningCmd{ExecCmd: command.ExecCmd{Command: cmd, Options: args}}
}

func (l *LightningHost) Import(opts *ImportOpts, timeout string) error {
	cfg := opts.Cfg
	args := make([]string, 0)
	if opts.TransferToCfg {
		// cfg = cfg + "\n" + opts.Config()
		var err error
		cfg, err = utils.MergeToml(cfg, opts.Config())
		if err != nil {
			return errors.New("Merge tomls failed")
		}
		log.Info(fmt.Sprintf("toml after merge: %s", cfg))
	} else {
		args = append(args, opts.Args()...)
	}
	if cfg != "" {
		filePath := "/tmp/tidb-lightning.toml"
		writeOpts := resource.WorkloadNodeWriteFileOptions{
			Content:  strings.NewReader(cfg),
			FilePath: filePath,
		}
		utils.Try(l.WriteFile(writeOpts))
		args = append(args, "-c", filePath)
	}
	output, err := l.ExecCmd(l.Lightning(args...).Compose(), timeout)
	if err != nil {
		return err
	}
	if output.ExitCode != 0 {
		logger.Error("Lightning failed")
		return errors.New("Lightning failed")
	}
	return nil
}

func (l *LightningHost) TiUPImport(opts *ImportOpts, version string, timeout string) error {
	cfg := opts.Cfg
	args := make([]string, 0)
	if opts.TransferToCfg {
		// cfg = cfg + "\n" + opts.Config()
		var err error
		cfg, err = utils.MergeToml(cfg, opts.Config())
		if err != nil {
			return errors.New("Merge tomls failed")
		}
		log.Info(fmt.Sprintf("toml after merge: %s", cfg))
	} else {
		args = append(args, opts.Args()...)
	}
	if cfg != "" {
		filePath := "/tmp/tidb-lightning.toml"
		writeOpts := resource.WorkloadNodeWriteFileOptions{
			Content:  strings.NewReader(cfg),
			FilePath: filePath,
		}
		utils.Try(l.WriteFile(writeOpts))
		args = append(args, "-c", filePath)
	}
	output, err := l.ExecCmd(l.TiUPLightning(version, args...).Compose(), timeout)
	if err != nil {
		return err
	}
	if output.ExitCode != 0 {
		logger.Error("Lightning failed")
		return errors.New("Lightning failed")
	}
	return nil
}

func NewLightningHost(res resource.Lightning) *LightningHost {
	return &LightningHost{Host{res}}
}
