package steps

import (
	"context"
	"database/sql"
	"fmt"
	"net/url"
	"regexp"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/caselib/pkg/host"
	brmodel "github.com/pingcap/test-infra/caselib/pkg/model/br"
	"github.com/pingcap/test-infra/caselib/pkg/mysql"
	"github.com/pingcap/test-infra/caselib/pkg/utils"
	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

type brBackupFull struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupFullOpts
}

func (b brBackupFull) String() string {
	return fmt.Sprintf("br backup full from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brBackupFull) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.BackupFull(pdUrl, b.opts)
}

var _ Step = &brBackupFull{}

type brTiUPBackupFull struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	version *string
	opts    *host.BackupFullOpts
}

func (b *brTiUPBackupFull) String() string {
	return fmt.Sprintf("tiup br %s backup full from tidb %+v on br host %+v with options %+v", *b.version, b.tidbRes, b.brRes, b.opts)
}

func (b *brTiUPBackupFull) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.TiUPBackupFull(pdUrl, *b.version, b.opts)
}

// Add this func for Parallel
func NewBrBackupFull(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) Step {
	return &brBackupFull{
		brRes:   brRes,
		tidbRes: tidbRes,
		opts:    opts,
	}
}

func (b *Builder) BrBackupFull(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) *Builder {
	b.steps = append(b.steps, brBackupFull{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

func NewTiUPBrBackupFull(brRes, tidbRes *apiresource.ResourceHandle, version *string, opts *host.BackupFullOpts) Step {
	return &brTiUPBackupFull{
		brRes:   brRes,
		tidbRes: tidbRes,
		version: version,
		opts:    opts,
	}
}

func (b *Builder) TiUPBrBackupFull(brRes, tidbRes *apiresource.ResourceHandle, version *string, opts *host.BackupFullOpts) *Builder {
	b.steps = append(b.steps, NewTiUPBrBackupFull(brRes, tidbRes, version, opts))
	return b
}

type brBackupDb struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupDbOpts
}

func (b brBackupDb) String() string {
	return fmt.Sprintf("br backup db from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brBackupDb) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.BackupDb(pdUrl, b.opts)
}

var _ Step = &brBackupDb{}

func (b *Builder) BrBackupDb(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupDbOpts) *Builder {
	b.steps = append(b.steps, brBackupDb{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

type brBackupTable struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupTableOpts
}

func (b brBackupTable) String() string {
	return fmt.Sprintf("br backup full from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brBackupTable) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.BackupTable(pdUrl, b.opts)
}

var _ Step = &brBackupTable{}

func (b *Builder) BrBackupTable(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupTableOpts) *Builder {
	b.steps = append(b.steps, brBackupTable{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

type brRestoreFull struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupFullOpts
}

func (b brRestoreFull) String() string {
	return fmt.Sprintf("br restore full from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brRestoreFull) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.RestoreFull(pdUrl, b.opts)
}

// Add this func for Parallel br restore in downstream tidb and cdc
func NewBrRestoreFull(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) Step {
	return &brRestoreFull{
		brRes:   brRes,
		tidbRes: tidbRes,
		opts:    opts,
	}
}

type brTiUPRestoreFull struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	version *string
	opts    *host.BackupFullOpts
}

func (b *brTiUPRestoreFull) String() string {
	return fmt.Sprintf("br restore full from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b *brTiUPRestoreFull) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.TiUPRestoreFull(pdUrl, *b.version, b.opts)
}

// Add this func for Parallel br restore in downstream tidb and cdc
func NewBrTiupRestoreFull(brRes, tidbRes *apiresource.ResourceHandle, version *string, opts *host.BackupFullOpts) Step {
	return &brTiUPRestoreFull{
		brRes:   brRes,
		tidbRes: tidbRes,
		version: version,
		opts:    opts,
	}
}

var _ Step = &brRestoreFull{}

func (b *Builder) BrRestoreFull(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) *Builder {
	b.steps = append(b.steps, brRestoreFull{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

func (b *Builder) TiUPBrRestoreFull(brRes, tidbRes *apiresource.ResourceHandle, version *string, opts *host.BackupFullOpts) *Builder {
	b.steps = append(b.steps, NewBrTiupRestoreFull(brRes, tidbRes, version, opts))
	return b
}

type brRestoreDb struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupDbOpts
}

func (b brRestoreDb) String() string {
	return fmt.Sprintf("br restore db from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brRestoreDb) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.RestoreDb(pdUrl, b.opts)
}

var _ Step = &brRestoreDb{}

func (b *Builder) BrRestoreDb(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupDbOpts) *Builder {
	b.steps = append(b.steps, brRestoreDb{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

func NewBrRestoreDb(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupDbOpts) Step {
	return &brRestoreDb{brRes: brRes, tidbRes: tidbRes, opts: opts}
}

type brRestoreTable struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupTableOpts
}

func (b brRestoreTable) String() string {
	return fmt.Sprintf("br restore full from tidb %s on br host %s with options %v", b.tidbRes, b.brRes, b.opts)
}

func (b brRestoreTable) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.RestoreTable(pdUrl, b.opts)
}

var _ Step = &brRestoreTable{}

func (b *Builder) BrRestoreTable(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupTableOpts) *Builder {
	b.steps = append(b.steps, brRestoreTable{brRes: brRes, tidbRes: tidbRes, opts: opts})
	return b
}

type brRestoreFullSystemTable struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupFullOpts
	table   *string
}

func (b brRestoreFullSystemTable) String() string {
	return fmt.Sprintf("br restore system tables %s to tidb %s on br host %s with options %v", *b.table, b.tidbRes.Name, b.brRes.Name, b.opts)
}

func (b brRestoreFullSystemTable) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	// restore full without -f mysql.*, and check mysql.* not restored.
	err := brHost.RestoreFull(pdUrl, b.opts)
	if err != nil {
		return err
	}
	mysqlInst := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.MySQLCompatible)
	db := utils.Try(mysqlInst.GetDB("")).(*sql.DB)
	defer db.Close()
	client := mysql.NewMySQLClient(db)
	var tableNum int
	sql := fmt.Sprintf("SELECT count(*) FROM information_schema.TABLES WHERE (TABLE_SCHEMA = 'mysql') AND (TABLE_NAME = '%s');", *b.table)
	err = client.QueryRow(sql).Scan(&tableNum)
	if err != nil {
		return err
	} else if tableNum != 0 {
		return errors.New("System table should not be restore without specifying filters.")
	}

	// restore full with -f mysql.*, it should restore the system table.
	b.opts.Filter = "mysql.*"
	return brHost.RestoreFull(pdUrl, b.opts)
}

func (b *Builder) BrRestoreSystemTable(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts, table *string) *Builder {
	b.steps = append(b.steps, brRestoreFullSystemTable{brRes: brRes, tidbRes: tidbRes, opts: opts, table: table})
	return b
}

type brBackupFullIncremental struct {
	brBackupFull
	// lastFullBackupLocation	*string
}

func (b brBackupFullIncremental) String() string {
	return fmt.Sprintf("Run br full incremental backup based on full backup %s", b.opts.Storage)
}

func (b brBackupFullIncremental) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	brHost := host.NewBrHost(brRes)
	// ts, err := brHost.GetTsFromBackup(b.lastFullBackupLocation)
	ts, err := brHost.GetTsFromBackup(&b.opts.Storage)
	if err != nil {
		return err
	}
	fmt.Println(fmt.Sprintf("Got last full backup ts: %s", ts))
	b.opts.BackupFullOptions = append(b.opts.BackupFullOptions, "--lastbackupts", ts)

	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)

	// Use storage specified by StorageIncremental for incremental backup
	optsIncr := *b.opts
	optsIncr.Storage = optsIncr.StorageIncremental
	return brHost.BackupFull(pdUrl, &optsIncr)
}

func (b *Builder) BrBackupFullIncremental(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) *Builder {
	brBackupFull := brBackupFull{brRes: brRes, tidbRes: tidbRes, opts: opts}
	b.steps = append(b.steps, brBackupFullIncremental{brBackupFull: brBackupFull})
	return b
}

// restore from incremental backup specified by op
type brRestoreFullIncremental struct {
	brRestoreFull
}

func (b brRestoreFullIncremental) String() string {
	return fmt.Sprintf("br restore full incremental to tidb %s on br host %s with options %v", b.tidbRes.Name, b.brRes.Name, b.opts)
}

func (b brRestoreFullIncremental) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	optsIncr := *b.opts
	//  Use storage specified by StorageIncremental for incremental backup restore
	optsIncr.Storage = optsIncr.StorageIncremental
	return brHost.RestoreFull(pdUrl, &optsIncr)
}

var _ Step = &brRestoreFullIncremental{}

func (b *Builder) BrRestoreFullIncremental(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupFullOpts) *Builder {
	brRestoreFull := brRestoreFull{brRes: brRes, tidbRes: tidbRes, opts: opts}
	b.steps = append(b.steps, brRestoreFullIncremental{brRestoreFull})
	return b
}

// restore from incremental backup specified by op
type brSQLBackupDB struct {
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupDbOpts
}

func NewBrSQLBackupDB(tidbRes *apiresource.ResourceHandle, opts *host.BackupDbOpts) *brSQLBackupDB {
	return &brSQLBackupDB{
		tidbRes: tidbRes,
		opts:    opts,
	}
}

func (b brSQLBackupDB) String() string {
	return fmt.Sprintf("br backup sql to tidb %s on with options %v", b.tidbRes.Name, b.opts)
}

func (b brSQLBackupDB) Execute(ctx context.Context) error {
	sql := fmt.Sprintf("backup database %s to \"%s\"", b.opts.Database, b.opts.Storage)
	sqlCmd := NewExecuteSQL(b.tidbRes, b.opts.Database, sql)
	if err := sqlCmd.Execute(ctx); err != nil {
		return errors.Trace(err)
	}
	return nil
}

var _ Step = &brSQLBackupDB{}

func (b *Builder) BrSQLBackupDB(tidbRes *apiresource.ResourceHandle, opts *host.BackupDbOpts) *Builder {
	b.steps = append(b.steps, brSQLBackupDB{tidbRes: tidbRes, opts: opts})
	return b
}

type brOperateLog struct {
	brRes     *apiresource.ResourceHandle
	tidbRes   *apiresource.ResourceHandle
	operation brmodel.OperateBrLogType
	opts      *host.BackupLogOpts
}

func (b *brOperateLog) String() string {
	return fmt.Sprintf("br %s log task from tidb %+v with options %+v", b.operation.String(), b.tidbRes, b.opts)
}

func (b *brOperateLog) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.BackupLog(b.operation, pdUrl, b.opts)
}

func (b *Builder) CreateBrLogTask(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) *Builder {
	b.steps = append(b.steps, &brOperateLog{brRes, tidbRes, brmodel.Start, opts})
	return b
}

func (b *Builder) PauseBrLogTask(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) *Builder {
	b.steps = append(b.steps, &brOperateLog{brRes, tidbRes, brmodel.Pause, opts})
	return b
}

func (b *Builder) ResumeBrLogTask(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) *Builder {
	b.steps = append(b.steps, &brOperateLog{brRes, tidbRes, brmodel.Resume, opts})
	return b
}

func (b *Builder) StopBrLogTask(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) *Builder {
	b.steps = append(b.steps, &brOperateLog{brRes, tidbRes, brmodel.Stop, opts})
	return b
}

func (b *Builder) QueryBrLogStatus(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) *Builder {
	b.steps = append(b.steps, &brOperateLog{brRes, tidbRes, brmodel.Status, opts})
	return b
}

func NewStopBrLogTask(brRes, tidbRes *apiresource.ResourceHandle, opts *host.BackupLogOpts) Step {
	return &brOperateLog{brRes, tidbRes, brmodel.Stop, opts}
}

type brRestorePoint struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.RestorePointOpts
}

func (b *brRestorePoint) String() string {
	return fmt.Sprintf("br restore point to tidb %+v with options %+v", b.tidbRes, b.opts)
}

func (b *brRestorePoint) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(b.brRes)).(resource.WorkloadNode)
	tidbRes := utils.Try(core.ResolveResourceHandle(b.tidbRes)).(resource.TiDBCluster)
	brHost := host.NewBrHost(brRes)
	pdUrl := utils.Try(tidbRes.ServiceURL(resource.PDAddr)).(*url.URL)
	return brHost.RestorePoint(pdUrl, b.opts)
}

func (b *Builder) BrRestorePoint(brRes, tidbRes *apiresource.ResourceHandle, opts *host.RestorePointOpts) *Builder {
	b.steps = append(b.steps, &brRestorePoint{brRes, tidbRes, opts})
	return b
}

func NewBrRestorePoint(brRes, tidbRes *apiresource.ResourceHandle, opts *host.RestorePointOpts) Step {
	return &brRestorePoint{brRes, tidbRes, opts}
}

type waitLogBackupCheckpoint struct {
	brRes   *apiresource.ResourceHandle
	tidbRes *apiresource.ResourceHandle
	opts    *host.BackupLogOpts
	ts      *string // fmt in "2006-01-02 15:04:05"
	timeout time.Duration
}

func (w waitLogBackupCheckpoint) String() string {
	return fmt.Sprintf("Wait until log backup checkpoint move forward to ts: %s; timeout: %s", *w.ts, w.timeout)
}

func (w waitLogBackupCheckpoint) Execute(ctx context.Context) error {
	timeoutTimer := time.NewTimer(w.timeout)
	intervalTicker := time.NewTicker(30 * time.Second)
	defer func() {
		timeoutTimer.Stop()
		intervalTicker.Stop()
	}()
	var err error
	waitCheckPoint, _ := time.Parse("2006-01-02 15:04:05", *w.ts)
LOOP:
	for {
		select {
		case <-timeoutTimer.C:
			err = errors.New("waitLogBackupCheckpoint timeout")
			break LOOP
		case <-intervalTicker.C:
			logOperator := brOperateLog{w.brRes, w.tidbRes, brmodel.Status, w.opts}
			if lerr := logOperator.Execute(ctx); lerr != nil {
				log.Error("query br log status fail", zap.Error(lerr))
				continue
			}
			re := regexp.MustCompile(`checkpoint\[.*global\]: (.*:\d\d)`)
			cpt := re.FindStringSubmatch(w.opts.Stdout)[1]
			currentCheckPoint, _ := time.Parse("2006-01-02 15:04:05", cpt)
			log.Info(fmt.Sprintf("Current log backup checkpoint: %s, expect it is larger than: %s", currentCheckPoint, waitCheckPoint))
			if currentCheckPoint.After(waitCheckPoint) {
				log.Info("waitLogBackupCheckpoint success", zap.Time("current checkpoint", currentCheckPoint))
				break LOOP
			}
		}
	}
	return err
}

func (b *Builder) WaitLogBackupCheckpoint(brRes, srcTidb *apiresource.ResourceHandle, opts *host.BackupLogOpts, ts *string, timeout time.Duration) *Builder {
	b.steps = append(b.steps, &waitLogBackupCheckpoint{brRes, srcTidb, opts, ts, timeout})
	return b
}

func NewWaitLogBackupCheckpoint(brRes, srcTidb *apiresource.ResourceHandle, opts *host.BackupLogOpts, ts *string, timeout time.Duration) Step {
	return &waitLogBackupCheckpoint{brRes, srcTidb, opts, ts, timeout}
}

type getTsFromSnapShot struct {
	brRes *apiresource.ResourceHandle
	path  *string
	ts    *string
}

func (g *getTsFromSnapShot) Execute(ctx context.Context) error {
	brRes := utils.Try(core.ResolveResourceHandle(g.brRes)).(resource.WorkloadNode)
	brHost := host.NewBrHost(brRes)
	str, err := brHost.GetTsFromBackup(g.path)
	if err != nil {
		return errors.Trace(err)
	} else {
		*g.ts = str
		return nil
	}
}

func (g *getTsFromSnapShot) String() string {
	return fmt.Sprintf("getTsFromBackupSnapShot: path=%s", *g.path)
}

func NewGetTsFromSnapShot(brRes *apiresource.ResourceHandle, path, ts *string) Step {
	return &getTsFromSnapShot{brRes, path, ts}
}

func (b *Builder) GetTsFromSnapShot(brRes *apiresource.ResourceHandle, path, ts *string) *Builder {
	b.steps = append(b.steps, NewGetTsFromSnapShot(brRes, path, ts))
	return b
}
