package main

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/pingcap/test-infra/caselib/pkg/host"
	sdiconfig "github.com/pingcap/test-infra/caselib/pkg/model/dm"
	"github.com/pingcap/test-infra/caselib/pkg/model/workload"
	"github.com/pingcap/test-infra/caselib/pkg/steps"
	"github.com/pingcap/test-infra/caselib/pkg/utils"
	apiresource "github.com/pingcap/test-infra/common/model/resource"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"
	"go.uber.org/zap"
)

var (
	srcTidb                 = &apiresource.ResourceHandle{}
	dstTidb                 = &apiresource.ResourceHandle{}
	brHost                  = &apiresource.ResourceHandle{}
	mcHost                  = &apiresource.ResourceHandle{}
	syncDiff                = &apiresource.ResourceHandle{}
	fullBackupOpts          = &host.BackupFullOpts{}
	mcRmOpts                = &host.McRmOpts{}
	globalVariableOpts      = steps.TiDBVariableList
	logBackupOpts           = &host.BackupLogOpts{}
	logRestoreOpts          = &host.RestorePointOpts{}
	workloadRes             = &apiresource.ResourceHandle{}
	syncDiffConfigs         = &sdiconfig.Config{}
	uniq                    = uuid.New().String()
	uniqFull                = fmt.Sprintf("%s/Full", uniq)
	uniqLog                 = fmt.Sprintf("%s/Log", uniq)
	localSyncDiffConfigFile = "sync_diff.yaml"
	targetDatabase          = "ppdb1_2"
	targetDatabase2         = "ppdb5_2"
	workloadKeyword         = "go-ycsb"
	emptyStr                string
	tsNow                   string
	tsHumanReadable         string
)

func CleanUp() steps.Step {
	return steps.
		NewBuilder().
		LoadEnv().
		LoadResourceFromEnv(utils.ResourcePrefix+"src_tidb", srcTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"br", brHost).
		LoadResourceFromEnv(utils.ResourcePrefix+"workload", workloadRes).
		LoadResourceFromEnv(utils.ParamPrefix+"br_log_opts", logBackupOpts).
		IgnoreError(steps.NewStopBrLogTask(brHost, srcTidb, logBackupOpts)).
		// Stop running workload if any
		IgnoreError(steps.NewStopWorkload(&workloadKeyword, workloadRes)).
		Build()
}

func BrPiTR() steps.Step {
	return steps.
		NewBuilder().
		LoadEnv().
		LoadResourceFromEnv(utils.ResourcePrefix+"src_tidb", srcTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"dst_tidb", dstTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"br", brHost).
		LoadResourceFromEnv(utils.ResourcePrefix+"syncdiff", syncDiff).
		LoadResourceFromEnv(utils.ResourcePrefix+"mc", mcHost).
		LoadResourceFromEnv(utils.ResourcePrefix+"workload", workloadRes).
		LoadSyncDiffConfig(&localSyncDiffConfigFile, syncDiffConfigs).
		LoadResourceFromEnv(utils.ParamPrefix+"global_variable", &globalVariableOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"mc_rm_opts", mcRmOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"br_log_opts", logBackupOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"br_opts", fullBackupOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"br_log_restore_opts", logRestoreOpts).
		NewUniqPath(&fullBackupOpts.Storage, uniqFull).
		NewUniqPath(&logBackupOpts.Storage, uniqLog).
		NewUniqPath(&logRestoreOpts.Storage, uniqLog).
		UpdateStructFieldByPointer(logRestoreOpts, "FullBackupStorage", &fullBackupOpts.Storage).
		NewUniqPath(&mcRmOpts.Path, uniq).
		DropUserDatabases(srcTidb).
		DropUserDatabases(dstTidb).
		ConfigGlobalVariable(&globalVariableOpts, srcTidb).
		CreateBrLogTask(brHost, srcTidb, logBackupOpts).
		QueryBrLogStatus(brHost, srcTidb, logBackupOpts).
		BrBackupFull(brHost, srcTidb, fullBackupOpts).
		// run paypay workload
		CreateDatabase(srcTidb, targetDatabase).
		InitWorkload(workloadRes).
		RunWorkloadAsync(srcTidb, workloadRes, &workload.PayPayOption{
			Command:        workload.CommandTypeLoad,
			PropertyFile:   "",
			PropertyValues: []string{"workload=wallet", "db1name=" + targetDatabase, "db5name=" + targetDatabase2, "mysql.db=" + targetDatabase, "operationcount=1000000000", "recordcount=10000", "ignore1=true", "ignore5=true", "clientcount=1", "clientid=0"},
			Threads:        5,
			Duration:       20 * time.Minute,
		}, false, true).
		RunWorkloadAsync(srcTidb, workloadRes, &workload.PayPayOption{
			Command:        workload.CommandTypeLoad,
			PropertyFile:   "",
			PropertyValues: []string{"workload=wallet", "db1name=" + targetDatabase, "db5name=" + targetDatabase2, "mysql.db=" + targetDatabase, "operationcount=1000000000", "recordcount=10000", "ignore1=false", "ignore5=true", "clientcount=1", "clientid=0"},
			Threads:        5,
			Duration:       20 * time.Minute,
		}, false, true).
		RunWorkloadAsync(srcTidb, workloadRes, &workload.PayPayOption{
			Command:        workload.CommandTypeRun,
			PropertyFile:   "",
			PropertyValues: []string{"workload=wallet", "db1name=" + targetDatabase, "db5name=" + targetDatabase2, "mysql.db=" + targetDatabase, "operationcount=1000000000", "recordcount=10000", "ignore1=false", "ignore5=true", "clientcount=1", "clientid=0"},
			Threads:        5,
			Duration:       99 * time.Hour,
		}, true, true).
		Sleep(5*time.Second).
		GetTsNow(srcTidb, &tsNow, &tsHumanReadable).
		WaitLogBackupCheckpoint(brHost, srcTidb, logBackupOpts, &tsHumanReadable, 20*time.Minute).
		UpdateStructFieldByPointer(logRestoreOpts, "RestoredTs", &tsNow).
		BrRestorePoint(brHost, dstTidb, logRestoreOpts).
		SyncDiffInspectorCheck(syncDiff, srcTidb, dstTidb, syncDiffConfigs, &tsNow, &emptyStr, 60*time.Minute).
		RepeatSteps(8,
			steps.NewUniqPath(&fullBackupOpts.Storage, time.Now().Format("20060102150405")),
			steps.NewUpdateStructFieldByPointer(logRestoreOpts, "FullBackupStorage", &fullBackupOpts.Storage),
			steps.NewBrBackupFull(brHost, srcTidb, fullBackupOpts),
			steps.NewSleepTask(60*time.Minute),
			steps.NewCheckWorkloadRunning(&workloadKeyword, workloadRes),
			steps.NewGetTsNow(srcTidb, &tsNow, &tsHumanReadable),
			steps.NewWaitLogBackupCheckpoint(brHost, srcTidb, logBackupOpts, &tsHumanReadable, 20*time.Minute),
			steps.NewUpdateStructFieldByPointer(logRestoreOpts, "RestoredTs", &tsNow),
			steps.DropUserDatabases(dstTidb),
			steps.NewSleepTask(10*time.Second),
			steps.NewBrRestorePoint(brHost, dstTidb, logRestoreOpts),
			steps.NewSyncDiffInspectorCheck(syncDiff, srcTidb, dstTidb, syncDiffConfigs, &tsNow, &emptyStr, 60*time.Minute),
		).
		McRemove(mcRmOpts, mcHost).
		Build()
}

func main() {
	_, logger := utils.GetCtxAndLogger()
	ctx := steps.New(context.Background())
	if err := CleanUp().Execute(ctx); err != nil {
		logger.Fatal("Cleanup failed")
	}
	if err := BrPiTR().Execute(ctx); err != nil {
		logger.Fatal("Br PiTR failed", zap.Error(err))
	}
	logger.Info("Br PiTR passed")
}
