package main

import (
	"context"
	"time"

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

func DumplingSnapShotLock() steps.Step {
	var (
		srcTidb            = &resource.ResourceHandle{}
		dstTidb            = &resource.ResourceHandle{}
		lightningRes       = &resource.ResourceHandle{}
		opts               = &steps.LightningImportOpts{}
		globalVariableOpts = &steps.TiDBVariableList
		dumplingRes        = &resource.ResourceHandle{}
		mcRes              = &resource.ResourceHandle{}
		mcRmOpts           = &host.McRmOpts{}
		dumplingOpts       = &host.DumplingGeneralOpts{}
		uniq               = uuid.New().String()
		dropDatabases      string
		adminCheck         string
		periodTaskLifetime = 1 * time.Minute
		periodTaskCron     = 5 * time.Second
		delay              = 15 * time.Second
	)
	return steps.
		NewBuilder().
		LoadEnv().
		LoadParamFromEnv(utils.ParamPrefix+"drop_databases", &dropDatabases).
		LoadResourceFromEnv(utils.ParamPrefix+"import_basic_opts", opts).
		LoadResourceFromEnv(utils.ResourcePrefix+"src_tidb", srcTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"dst_tidb", dstTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"lightning", lightningRes).
		LoadParamFromEnv(utils.ParamPrefix+"admin_check", &adminCheck).
		LoadResourceFromEnv(utils.ParamPrefix+"global_variable", &globalVariableOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"dumpling_opts", dumplingOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"mc_rm_opts", mcRmOpts).
		LoadResourceFromEnv(utils.ResourcePrefix+"dumpling", dumplingRes).
		LoadResourceFromEnv(utils.ResourcePrefix+"mc", mcRes).
		ConfigGlobalVariable(globalVariableOpts, srcTidb).
		// enable enable-table-lock so that dumpling can lock table.
		UpdateTiDBClusterConfig(srcTidb, sdkResource.TiDB, "enable-table-lock = true", 5*time.Minute).
		DropDatabasesIfExists(srcTidb, &dropDatabases).
		DropUserDatabases(srcTidb).
		LightningImport(opts, lightningRes, srcTidb).
		CheckDatabasesCrc32(&opts.Crc32Checksum, &dropDatabases, srcTidb).
		AdminCheck(srcTidb, &adminCheck).
		NewUniqPath(&dumplingOpts.Output, uniq).
		NewUniqPath(&mcRmOpts.Path, uniq).
		// Start dumpling, and drop databases with delay to make sure drop table is after dumpling start.
		// Drop table Periodically and ignore error as drop table might temp fail due to table lock.
		Parallel(
			steps.NewDumpling(dumplingRes, srcTidb, dumplingOpts),
			steps.NewDelayTask(delay,
				steps.NewPeriodicalTask(periodTaskLifetime, periodTaskCron,
					steps.NewIgnoreError(steps.NewDropDatabasesIfExists(srcTidb, &dropDatabases)),
				)),
		).
		ConfigGlobalVariable(globalVariableOpts, dstTidb).
		DropUserDatabases(dstTidb).
		LightningImportFromDumpling(opts, lightningRes, dstTidb, &dumplingOpts.Output).
		CheckDatabasesCrc32(&opts.Crc32Checksum, &dropDatabases, dstTidb).
		AdminCheck(dstTidb, &adminCheck).
		McRemove(mcRmOpts, mcRes).
		Build()
}

func main() {
	_, logger := utils.GetCtxAndLogger()
	ctx := steps.New(context.Background())
	if err := DumplingSnapShotLock().Execute(ctx); err != nil {
		logger.Fatal("DumplingSnapShotLock failed", zap.Error(err))
	}
	logger.Info("DumplingSnapShotLock finished")
}
