package quantify_products

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	. "github.com/pingcap/endless/pkg/matcher"
	"github.com/pingcap/test-infra/caselib/pkg/command"
	"github.com/pingcap/test-infra/caselib/pkg/host"
	"github.com/pingcap/test-infra/caselib/pkg/model/workload"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
)

type TopologyConfig struct {
	NumPD   uint
	NumTiKV uint
	NumTiDB uint
}

func PDServerConfig(idx uint) string {
	return fmt.Sprintf(`  - host: pd%d-peer
    client_port: 12379
    peer_port: 1238`, idx)
}

func TiKVServerConfig(idx uint) string {
	return fmt.Sprintf(`  - host: tikv%d-peer
    port: 30160
    status_port: 30180`, idx)
}

func TiDBServerConfig(idx uint) string {
	return fmt.Sprintf(`  - host: tidb%d-peer
    port: 14000
    status_port: 2008`, idx)
}

/// InitTopologyFile will write an tiup.yaml file into wkload node.
///
/// TESTBED REQUIRED:
///     1. all cluster components has a name with suffix starts from 1, eg:
///			pd1, pd2, ....
///			tikv1, tikv2, ... tikvn
///			tidb1
///		2. the name of workload node is 'wkload'
func InitTopologyFile(ctx core.TestContext, topoCfg *TopologyConfig) string {
	if len(os.Getenv("DEBUG_TOPOLOGY_FILE")) != 0 {
		cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)

		filename := fmt.Sprintf("%d.%s", time.Now().Unix(), "tiup.yaml")
		filepath := fmt.Sprintf("/tmp/%s", filename)
		file := Try(os.Open(os.Getenv("DEBUG_TOPOLOGY_FILE"))).(*os.File)
		opts := resource.WorkloadNodeWriteFileOptions{
			Content:  file,
			FilePath: filepath,
		}
		Try(cntlNode.WriteFile(opts))

		return filepath
	}

	pdServers := []string{}
	kvServers := []string{}
	dbServers := []string{}
	for idx := uint(1); idx <= topoCfg.NumPD; idx++ {
		pdServers = append(pdServers, PDServerConfig(idx))
	}
	for idx := uint(1); idx <= topoCfg.NumTiKV; idx++ {
		kvServers = append(kvServers, TiKVServerConfig(idx))
	}
	for idx := uint(1); idx <= topoCfg.NumTiDB; idx++ {
		dbServers = append(dbServers, TiDBServerConfig(idx))
	}

	tiupTopo := fmt.Sprintf(`
global:
  user: "tidb"
  ssh_port: 22
  deploy_dir: "/tiup/deploy"
  data_dir: "/tiup/data"
  arch: "amd64"

monitored:
  node_exporter_port: 19100
  blackbox_exporter_port: 19115

pd_servers:
%s

tidb_servers:
%s

tikv_servers:
%s

monitoring_servers:
  - host: wkload-peer
    port: 19090

grafana_servers:
  - host: wkload-peer
    port: 13000
`,
		strings.Join(pdServers, "\n"),
		strings.Join(dbServers, "\n"),
		strings.Join(kvServers, "\n"))

	cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)

	filename := fmt.Sprintf("%d.%s", time.Now().Unix(), "tiup.yaml")
	filepath := fmt.Sprintf("/tmp/%s", filename)
	opts := resource.WorkloadNodeWriteFileOptions{
		Content:  strings.NewReader(tiupTopo),
		FilePath: filepath,
	}
	Try(cntlNode.WriteFile(opts))

	return filepath
}

func SetupTiUPEnv(ctx core.TestContext) {
	// cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)
	// cntlHost := host.NewWorkloadHost(cntlNode)

	// cntlHost.Run(
	// 	workload.NewCMD("tiup update --all --self >/dev/null 2>&1", 5*time.Minute))
}

func TiCatAddHub(ctx core.TestContext, hosts ...string) {
	commands := make([]string, 0, len(hosts))
	for _, host := range hosts {
		if len(host) == 0 {
			continue
		}
		commands = append(commands, fmt.Sprintf("/root/ticat hub.+ %s;", host))
	}
	if len(commands) == 0 {
		return
	}

	cmd := strings.Join(commands, "\n")
	cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)
	cntlHost := host.NewWorkloadHost(cntlNode)
	Try(cntlHost.Run(workload.NewCMD(cmd, time.Duration(5*len(hosts))*time.Minute)))
}

func SetupTiCatEnv(ctx core.TestContext, disableRecover bool) {
	cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)
	cntlHost := host.NewWorkloadHost(cntlNode)

	recover := ""
	if disableRecover {
		recover = "debug.recover.off :"
	}
	cmd := fmt.Sprintf(`
	/root/ticat %s display.color.off : display.width 100 : display.utf8.off : env.save;
	/root/ticat hub.update-all;`, recover)
	Try(cntlHost.Run(
		workload.NewCMD(cmd, 5*time.Minute)))

	if len(os.Getenv("INSTALL_SSH_ID")) != 0 {
		result := Try(cntlHost.Exec("cat ~/.ssh/id_rsa.pub", time.Minute)).(*command.ExecOutput)
		Try(os.WriteFile("/tmp/id_rsa.pub", []byte(result.Stdout), 0644))
		ipList := strings.Split(os.Getenv("INSTALL_SSH_ID"), ",")
		for _, ip := range ipList {
			res := exec.Command(
				"sshpass", "-p", "pingcap!@#",
				"ssh-copy-id", "-f", "-i", "/tmp/id_rsa.pub",
				"-o", "StrictHostKeyChecking=no",
				fmt.Sprintf("root@%s", ip))
			Try(res.Run())
		}
	}
}

func StopAllBenchTools(ctx core.TestContext) {
	cntlNode := ctx.Resource("wkload").(resource.WorkloadNode)
	cntlHost := host.NewWorkloadHost(cntlNode)
	cntlHost.Run(workload.NewCMD(`
pkill -9 sysbench >/dev/null 2>&1 || true;
pkill -9 go-ycsb >/dev/null 2>&1 || true;
pkill -9 tiup >/dev/null 2>&1 || true;
	`, 5*time.Minute))
}

func Setup(ctx core.TestContext, disableRecover bool) {
	SetupTiUPEnv(ctx)
	SetupTiCatEnv(ctx, disableRecover)
}
