package host

import (
	"net/url"
	"strconv"

	"github.com/pingcap/errors"
	"github.com/pingcap/test-infra/sdk/resource"
)

func appendOpts(original, option, value string) string {
	if value != "" {
		return original + " " + option + " \"" + value + "\""
	}
	return original
}

type MySQLOpts struct {
	Host     string `json:"host"`
	Port     string `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
}

func (m *MySQLOpts) Opts() string {
	opts := ""
	opts = appendOpts(opts, "-h", m.Host)
	opts = appendOpts(opts, "-P", m.Port)
	opts = appendOpts(opts, "-u", m.User)
	opts = appendOpts(opts, "-p", m.Password)
	return opts
}

func NewMySQLOpts(mysqlUrl *url.URL) *MySQLOpts {
	password, _ := mysqlUrl.User.Password()
	return &MySQLOpts{
		Host:     mysqlUrl.Hostname(),
		Port:     mysqlUrl.Port(),
		User:     mysqlUrl.User.Username(),
		Password: password,
	}
}

// DumplingGeneralOpts json example: {"output":"/tmp/dumpling-output","filetype":"csv","filesize":"256M","rows":200,"threads":20,"timeout":"10m","options":"-f test.order*"}
type DumplingGeneralOpts struct {
	Output      string `json:"output"`
	FileType    string `json:"filetype"`
	FileSize    string `json:"filesize"`
	Rows        int    `json:"rows"`
	Threads     int    `json:"threads"`
	Consistency string `json:"consistency"`
	Timeout     string `json:"timeout"`
	Options     string `json:"options"`
}

func (d *DumplingGeneralOpts) Opts() string {
	opts := ""
	opts = appendOpts(opts, "--output", d.Output)
	opts = appendOpts(opts, "--filetype", d.FileType)
	opts = appendOpts(opts, "--filesize", d.FileSize)
	if d.Rows != 0 {
		opts = appendOpts(opts, "--rows", strconv.Itoa(d.Rows))
	}
	if d.Threads != 0 {
		opts = appendOpts(opts, "--threads", strconv.Itoa(d.Threads))
	}
	opts = appendOpts(opts, "--consistency", d.Consistency)
	opts = opts + " " + d.Options
	return opts
}

type DumplingOpts struct {
	mysqlOpts           *MySQLOpts
	dumplingGeneralOpts *DumplingGeneralOpts
}

func (d *DumplingOpts) Command() string {
	return "/dumpling " + d.mysqlOpts.Opts() + d.dumplingGeneralOpts.Opts()
}

func (d *DumplingOpts) TiUPCommand(version string) string {
	var cmd string
	if version == "" {
		cmd = "tiup dumpling "
	} else {
		cmd = "tiup dumpling:" + version + " "
	}
	return cmd + d.mysqlOpts.Opts() + d.dumplingGeneralOpts.Opts()
}

func NewDumplingOpts(mysqlUrl *url.URL, opts *DumplingGeneralOpts) *DumplingOpts {
	return &DumplingOpts{
		mysqlOpts:           NewMySQLOpts(mysqlUrl),
		dumplingGeneralOpts: opts,
	}
}

type DumplingHost struct {
	Host
}

func (d *DumplingHost) Dump(mysqlUrl *url.URL, opts *DumplingGeneralOpts) error {
	output, err := d.ExecCmd(NewDumplingOpts(mysqlUrl, opts).Command(), opts.Timeout)
	if err != nil {
		return errors.Trace(err)
	}
	if output.ExitCode != 0 {
		logger.Info("Dumpling failed")
		return errors.New("Dumpling failed")
	}
	return nil
}

func (d *DumplingHost) TiUPDump(mysqlUrl *url.URL, version string, opts *DumplingGeneralOpts) error {
	output, err := d.ExecCmd(NewDumplingOpts(mysqlUrl, opts).TiUPCommand(version), opts.Timeout)
	if err != nil {
		return errors.Trace(err)
	}
	if output.ExitCode != 0 {
		logger.Info("Dumpling failed")
		return errors.New("Dumpling failed")
	}
	return nil
}

func NewDumplingHost(res resource.WorkloadNode) *DumplingHost {
	return &DumplingHost{Host{res}}
}
