package main

import (
	"context"

	"github.com/pingcap/log"

	"github.com/coreos/go-semver/semver"
	"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"
	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{}
	lightningRes       = &apiresource.ResourceHandle{}
	opts               = &steps.LightningImportOpts{}
	backupOpts         = &host.BackupDbOpts{}
	mcRmOpts           = &host.McRmOpts{}
	uniq               = uuid.New().String()
	globalVariableOpts = &steps.TiDBVariableList
	adminCheck         string
	currentVersion     string
	previousVersion    string
)

func backupFromVersionAndThenRestoreToVersion(from, to string) func(b *steps.Builder) {
	return func(b *steps.Builder) {
		b.UpgradeTiDBAllComponent(srcTidb, &from, 0).
			UpgradeTiDBAllComponent(dstTidb, &to, 0).
			BrBackupDb(brHost, srcTidb, backupOpts).
			DropDatabasesIfExists(dstTidb, &backupOpts.Database).
			BrRestoreDb(brHost, dstTidb, backupOpts).
			CheckDatabasesCrc32(&backupOpts.Crc32Checksum, &backupOpts.Database, dstTidb).
			AdminCheck(dstTidb, &adminCheck).
			McRemove(mcRmOpts, mcHost)
	}
}

func loadEnvs(b *steps.Builder) {
	b.LoadEnv().
		LoadResourceFromEnv(utils.ResourcePrefix+"src_tidb", srcTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"dst_tidb", dstTidb).
		LoadResourceFromEnv(utils.ResourcePrefix+"lightning", lightningRes).
		LoadResourceFromEnv(utils.ResourcePrefix+"br", brHost).
		LoadResourceFromEnv(utils.ResourcePrefix+"mc", mcHost).
		LoadResourceFromEnv(utils.ParamPrefix+"import_basic_opts", opts).
		LoadResourceFromEnv(utils.ParamPrefix+"br_opts", backupOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"mc_rm_opts", mcRmOpts).
		LoadResourceFromEnv(utils.ParamPrefix+"global_variable", &globalVariableOpts).
		LoadParamFromEnv(utils.ParamPrefix+"admin_check", &adminCheck).
		LoadParamFromEnv(utils.ParamPrefix+"current_version", &currentVersion).
		LoadParamFromEnv(utils.ParamPrefix+"previous_version", &previousVersion)
}

func BrDataCompatibility() steps.Step {
	currentVer := utils.Try(semver.NewVersion(currentVersion)).(*semver.Version)
	previousVer := utils.Try(semver.NewVersion(previousVersion)).(*semver.Version)
	log.Info("Start testing compatibility between versions", zap.Stringer("this", currentVer), zap.Stringer("prev", previousVer))

	return steps.
		NewBuilder().
		NewUniqPath(&backupOpts.Storage, uniq).
		NewUniqPath(&mcRmOpts.Path, uniq).
		DropDatabasesIfExists(srcTidb, &backupOpts.Database).
		LightningImport(opts, lightningRes, srcTidb).
		Include(backupFromVersionAndThenRestoreToVersion("v"+currentVer.String(), "v"+previousVer.String())).
		Include(backupFromVersionAndThenRestoreToVersion("v"+previousVer.String(), "v"+currentVer.String())).
		Build()
}

func main() {
	_, logger := utils.GetCtxAndLogger()
	ctx := steps.New(context.Background())
	if err := steps.NewBuilder().Include(loadEnvs).Build().Execute(ctx); err != nil {
		logger.Fatal("Br Compatibility failed to load env", zap.Error(err))
	}
	if err := BrDataCompatibility().Execute(ctx); err != nil {
		logger.Fatal("Br Compatibility failed", zap.Error(err))
	}
	logger.Info("Br Compatibility passed")
}
