package server

import (
	"context"
	"io"

	"github.com/tidbcloud/observability/vm-migration/internal/migration"
)

// loopJob wraps a migration.Loop run pass as a job.
type loopJob struct {
	loop *migration.Loop
}

func (j *loopJob) Kind() jobKind { return jobMigrateRun }

func (j *loopJob) Run(ctx context.Context) error {
	return j.loop.Run(ctx)
}

func (j *loopJob) Warnings() []string { return j.loop.Warnings() }

// DefaultRunRetries is the retry budget POST /run applies when the request
// omits "retries"; serve-restart auto-resume dispatches with the same
// budget.
const DefaultRunRetries = 5

// StartLoopRun constructs a migration.Loop over the migration directory and
// dispatches a migrate-run job that retries up to retries more times when a pass
// ends in PartialFailure.
//
// StartLoopRun returns the dispatched job's ID and errAlreadyRunning if a job
// is already in flight.
func (m *Migration) StartLoopRun(retries int) (string, error) {
	loop, err := m.buildLoop()
	if err != nil {
		return "", err
	}
	interval := m.retryInterval
	if interval <= 0 {
		interval = retryInterval
	}
	return m.start(wrapRetry(&loopJob{loop: loop}, retries, m.logs, interval))
}

// buildLoop constructs a migration.Loop over the migration's plan file.
//
// The loop anchors all relative paths on the plan's directory (m.dir). The
// Progress and observed action output flow into the current job stream. The
// action log is also persisted in its per-tenant file by migration.Loop.
func (m *Migration) buildLoop() (*migration.Loop, error) {
	return migration.NewLoop(m.loopPath(), migration.LoopOutput{
		Result:            io.Discard,
		Progress:          m.logs,
		ActionLogObserver: migration.ActionLogObserver(m.logs.Stream),
	}, m.loopOptions...)
}
