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{}
	dropDatabases           = "sbtest1"
	logBackupOpts           = &host.BackupLogOpts{}
	logRestoreOpts          = &host.RestorePointOpts{}
	workloadRes             = &apiresource.ResourceHandle{}
	uniq                    = uuid.New().String()
	uniqFull                = fmt.Sprintf("%s/Full", uniq)
	uniqLog                 = fmt.Sprintf("%s/Log", uniq)
	localSyncDiffConfigFile = "sync_diff.yaml"
	syncDiffConfigs         = &sdiconfig.Config{}
	tsNow                   string
	tsHumanReadable         string
	emptyStr                string
)

func CleanUp() steps.Step {
	return steps.
		NewBuilder().
		LoadEnv().
		LoadResourceFromEnv(utils.ResourcePrefix+"src_tidb", srcTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"br", brHost).
		LoadResourceFromEnv(utils.ParamPrefix+"br_log_opts", logBackupOpts).
		IgnoreError(steps.NewStopBrLogTask(brHost, srcTidb, logBackupOpts)).
		Build()
}

// BrPiTR start log backup
// do full backup
// run sysbench workload
// wait checkpoint and run PiTR restore
// Run sync_diff_inspector to check data consistence
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+"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).
		Sleep(10*time.Second).
		// log backup
		CreateBrLogTask(brHost, srcTidb, logBackupOpts).
		// query log status
		QueryBrLogStatus(brHost, srcTidb, logBackupOpts).
		BrBackupFull(brHost, srcTidb, fullBackupOpts).
		// run sysbench
		CreateDatabase(srcTidb, dropDatabases).
		InitWorkload(workloadRes).
		RunBinlogWorkload(srcTidb, workloadRes, &workload.DmSysbenchOption{
			Command:    workload.CommandTypePrepare,
			DB:         dropDatabases,
			TableCount: 10,
			TableSize:  10000,
			TestName:   "oltp_insert",
			Threads:    20,
			Duration:   1 * time.Minute,
		}).
		RunBinlogWorkload(srcTidb, workloadRes, &workload.DmSysbenchOption{
			Command:    workload.CommandTypeRun,
			DB:         dropDatabases,
			TableCount: 10,
			TableSize:  10000,
			TestName:   "oltp_insert",
			Threads:    20,
			Duration:   10 * time.Minute,
		}).
		GetTsNow(srcTidb, &tsNow, &tsHumanReadable).
		WaitLogBackupCheckpoint(brHost, srcTidb, logBackupOpts, &tsHumanReadable, 10*time.Minute).
		// PiTR restore
		BrRestorePoint(brHost, dstTidb, logRestoreOpts).
		// sync_diff to check data consistency
		SyncDiffInspectorCheck(syncDiff, srcTidb, dstTidb, syncDiffConfigs, &emptyStr, &emptyStr, 60*time.Minute).
		McRemove(mcRmOpts, mcHost).
		Build()
}

func main() {
	_, logger := utils.GetCtxAndLogger()
	ctx := steps.New(context.Background())
	// stop log backup task, in case previous task not stopped
	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")
}
