package scalebench

import (
	"context"
	"strings"
	"time"

	. "github.com/onsi/gomega"
	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/endless/testcase/oltp/tiuptest/tiuppkg"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
	"golang.org/x/sync/errgroup"
)

var caseCmdParmsStrDefault string = `
{ workloadType: 
	{ type: [ 'sysbench_oltp_write_only' ],
	},
   tpccParms: 
	{ storage: 's3://benchmark/tpcc-1000-4.0',
	  db: 'tpcc',
	  warehouse: 1000,
	  threads: 512,
	  ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007' 
	  workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
	sysbenchParms: 
		{ storage: 's3://benchmark/sysbench-32-10000000',
		db: 'test',
		tableNum: 32,
		tableSize: 10000000,
		threads: 512,
		ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
		workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },		  
	ycsbParms:
	{ storage : s3://benchmark/ycsb-100m-v5.0.0  
	db : test
	recordCount : 100000000
	operationCount : 50000000
	threadCount : 100
	subType: workloada
	ignoreErrors : 2013,1213,1105,1205,8022,8028,9004,9007,1062 },
	  }`

type CaseRunParms struct {
	ScaleType     string
	Topo          string
	WithTidb      bool
	CleanEnv      bool
	ScaleTidbTopo string
}

func OLTP_SCALE_IN_BENCH_TPCC_RUN(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'tpcc'] },
tpccParms: 
{ storage: 's3://benchmark/tpcc-1000-4.0',
db: 'tpcc',
warehouse: 1000,
threads: 16,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007', 
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, CaseRunParms{ScaleType: "in",
		Topo:     TiupTopoConfig,
		WithTidb: true,
		CleanEnv: true})
}

func OLTP_SCALE_OUT_BENCH_TPCC_RUN(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'tpcc'] },
tpccParms: 
{ storage: 's3://benchmark/tpcc-1000-4.0',
db: 'tpcc',
warehouse: 1000,
threads: 64,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007', 
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m},  
}`

	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_OUT_BENCH_TPCC_RUN_HIGH(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'tpcc'] },
tpccParms: 
{ storage: 's3://benchmark/tpcc-1000-4.0',
db: 'tpcc',
warehouse: 1000,
threads: 128,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007', 
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_OUT_BENCH_TPCC_PREPARE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'tpcc_prepare'] },
tpccParms: 
{ storage: 's3://benchmark/tpcc-1000-4.0',
db: 'tpcc',
warehouse: 100000,
threads: 1024,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007', 
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_IN_BENCH_TPCC_PREPARE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'tpcc_prepare'] },
tpccParms: 
{ storage: 's3://benchmark/tpcc-1000-4.0',
db: 'tpcc',
warehouse: 10000,
threads: 256,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007', 
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007'},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_OUT_BENCH_SYSBENCH_READ_ONLY(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_read_only'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 10000000,
threads: 512,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittleRead,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidbLarge,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_IN_BENCH_SYSBENCH_READ_ONLY(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_read_only'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 10000000,
threads: 512,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig6Tidbs,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidbLarge,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_OUT_BENCH_SYSBENCH_PREPARE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_prepare'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 10000000,
threads: 512,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 90m},  
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_IN_BENCH_SYSBENCH_PREPARE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_prepare'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 1000000000,
threads: 16,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 20h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}
func OLTP_SCALE_OUT_BENCH_SYSBENCH_WRITE_ONLY(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_write_only'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 1000000000,
threads: 512,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}
func OLTP_SCALE_IN_BENCH_SYSBENCH_WRITE_ONLY(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_write_only'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 1000000000,
threads: 16,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 30m},  
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_IN_BENCH_SYSBENCH_READ_WRITE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_read_write'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 1000000000,
threads: 64,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 5h,
warmUpTime: 30m},  
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}
func OLTP_SCALE_OUT_BENCH_SYSBENCH_READ_WRITE(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'sysbench_oltp_read_write'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 1000000000,
threads: 1024,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_OUT_BENCH_YCSB(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'ycsb'] },
ycsbParms: 
{storage : 's3://benchmark/ycsb-100m-v5.0.0',
db : test,
recordCount : 1000000000,
operationCount : 500000000,
threadCount : 256,
subType: workloada},
runTime:
{totalRunTime: 10h,
warmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "out",
		Topo:          TiupTopoConfigLittle,
		WithTidb:      true,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func OLTP_SCALE_IN_BENCH_YCSB(ctx core.TestContext, version string) {
	var caseCmdParmsStr string = `
{ workloadType: 
{ type: [ 'ycsb'] },
sysbenchParms: 
{ storage: 's3://benchmark/sysbench-32-10000000',
db: 'test',
tableNum: 32,
tableSize: 10000000,
threads: 50,
ignoreErrors: '2013,1213,1105,1205,8022,8027,8028,9004,9007',
workloadCheckIgnoreErros: '2013,1213,1105,1205,8022,8027,8028,9004,9007' },	
runTime:
{ totalRunTime: 10h,
WarmUpTime: 90m}, 
}`
	caseRunParms := CaseRunParms{ScaleType: "in",
		Topo:          TiupTopoConfig,
		WithTidb:      false,
		ScaleTidbTopo: ScaleOutTopoTidb,
		CleanEnv:      true}
	BenchmarkCommon(ctx, "nightly", caseCmdParmsStr, caseRunParms)
}

func BenchmarkCommon(ctx core.TestContext, version string, caseCmdParmsStr string, caseRunParms CaseRunParms) {
	caseCmdParms := oltppkg.BuildCaseCmdParms(caseCmdParmsStr)

	controlerNode := ctx.Resource("tiup").(resource.WorkloadNode)
	benchtoolset := ctx.Resource("benchtoolset").(resource.WorkloadNode)

	tiupCluster := tiuppkg.InitTiupCluster(ctx, controlerNode, caseRunParms.Topo, ClusterName, version, "staging", caseRunParms.CleanEnv)
	ImportData(ctx, &tiupCluster, benchtoolset, caseCmdParms)

	//Try(tiupCluster.ScaleOut(ScaleOutTopoTidb))
	// WaitForBalance(ctx, tiupCluster, 0.1, 1*time.Minute, 10*time.Minute)
	// Try(tiuppkg.UploadClinic(&tiupCluster, "--include", "monitor,log", "-f", "-5h", "-R", "tikv,pd"))
	// return

	ctxAll, cancel := context.WithTimeout(ctx, caseCmdParms.RunTime.TotalRunTime)
	g, ctxg := errgroup.WithContext(ctxAll)

	oltppkg.StopWorkload(benchtoolset, "goTpc")
	oltppkg.StopWorkload(benchtoolset, "sysBench")

	g.Go(WithGinkgoRecover(func() {
	loop:
		for {
			select {
			case <-ctxg.Done():
				break loop
			default:
			}
			oltppkg.StopWorkload(benchtoolset, "goTpc")
			oltppkg.StopWorkload(benchtoolset, "sysBench")
			cmdCommand := BuildWorkloadRuncmd(ctxg, tiupCluster, benchtoolset, &caseCmdParms)
			oltppkg.ExecWorkloadCmd(ctxg, benchtoolset, cmdCommand, true, nil)
		}
	}))

	g.Go(WithGinkgoRecover(func() {
		if caseRunParms.ScaleType == "out" && caseRunParms.WithTidb {
			oltppkg.WaitFor(ctxg, "waiting for scale and restart workload", caseCmdParms.RunTime.WarmUpTime+20*time.Minute)
			oltppkg.StopWorkload(benchtoolset, "goTpc")
			oltppkg.StopWorkload(benchtoolset, "sysBench")
		}
	}))

	oltppkg.WaitFor(ctxg, "waiting for workload balance before scale", caseCmdParms.RunTime.WarmUpTime)
	if caseRunParms.ScaleType == "in" {
		ScaleInCluster(ctxg, tiupCluster, caseCmdParms.RunTime.TotalRunTime)
	} else {
		ScaleOutCluster(ctxg, tiupCluster, caseCmdParms.RunTime.TotalRunTime, caseRunParms.WithTidb, caseRunParms.ScaleTidbTopo)
	}

	oltppkg.WaitFor(ctxg, "waiting for workload running after do scale", 20*time.Minute)
	cancel()
	Expect(g.Wait()).Should(BeNil())

	ctx2, _ := context.WithTimeout(ctxg, 20*time.Minute)
	if caseRunParms.ScaleType == "out" {
		oltppkg.StopWorkload(benchtoolset, "goTpc")
		oltppkg.StopWorkload(benchtoolset, "sysBench")

		if strings.HasPrefix(caseCmdParms.WorkloadType.Type[0], "sysbench") {
			caseCmdParms.SysbenchParms.Threads = caseCmdParms.SysbenchParms.Threads * 2
		}
		if strings.HasPrefix(caseCmdParms.WorkloadType.Type[0], "tpcc") {
			caseCmdParms.TpccParms.Threads = caseCmdParms.TpccParms.Threads * 2
		}
		if strings.HasPrefix(caseCmdParms.WorkloadType.Type[0], "ycsb") {
			caseCmdParms.YcsbParms.ThreadCount = caseCmdParms.YcsbParms.ThreadCount * 2
		}
		cmdCommand := BuildWorkloadRuncmd(ctx2, tiupCluster, benchtoolset, &caseCmdParms)
		oltppkg.ExecWorkloadCmd(ctx2, benchtoolset, cmdCommand, true, nil)
	}

	//Try(tiuppkg.UploadClinic(&tiupCluster, "--include", "monitor,log", "-f", "-5h", "-R", "tikv,pd"))
	//Try(tiupCluster.Destroy())
}
