package cluster

import "context"

// Step is the interface that each lifecycle step must implement.
type Step interface {
	// Name returns the step identifier.
	Name() StepName
	// Run executes the step. It should respect context cancellation.
	Run(ctx context.Context) error
}

// StateAwareStep can mutate persisted lifecycle metadata while it runs.
type StateAwareStep interface {
	RunWithState(ctx context.Context, state *State) error
}

// Phase groups steps that run together. Steps within a parallel phase
// execute concurrently; sequential phases have exactly one step.
type Phase struct {
	Steps    []Step
	Parallel bool
}

// Plan returns the ordered execution phases for the legacy cp9/v1 lifecycle.
func Plan(steps map[StepName]Step) []Phase {
	return PlanForLifecycle(LifecycleLegacyV2, steps)
}

// PlanForLifecycle returns the ordered execution phases for one lifecycle schema.
func PlanForLifecycle(kind LifecycleKind, steps map[StepName]Step) []Phase {
	if kind == LifecycleV3 {
		order := StepsForLifecycle(kind)
		phases := make([]Phase, 0, len(order)-3)
		for _, name := range order {
			if name == StepSessionReady {
				phases = append(phases, Phase{
					Steps: []Step{
						steps[StepSessionReady],
						steps[StepStarterActivation],
						steps[StepEssentialV2Activation],
						steps[StepBYOCActivation],
					},
					Parallel: true,
				})
				continue
			}
			if name == StepStarterActivation ||
				name == StepEssentialV2Activation ||
				name == StepBYOCActivation {
				continue
			}
			phases = append(phases, Phase{Steps: []Step{steps[name]}})
		}
		return phases
	}
	return PlanForOrder(StepsForLifecycle(kind), steps)
}

// PlanForOrder converts an ordered step list into the sequential execution phases.
func PlanForOrder(order []StepName, steps map[StepName]Step) []Phase {
	get := func(name StepName) Step { return steps[name] }
	phases := make([]Phase, 0, len(order))
	for _, name := range order {
		phases = append(phases, Phase{Steps: []Step{get(name)}})
	}
	return phases
}

// NoopStep is a placeholder step that succeeds immediately.
// Used for steps whose real implementation lives in a different package
// that hasn't been built yet.
type NoopStep struct {
	StepName StepName
}

func (n *NoopStep) Name() StepName              { return n.StepName }
func (n *NoopStep) Run(_ context.Context) error { return nil }

// DefaultSteps returns a map of all steps using noop implementations.
// Callers can replace individual steps with real implementations.
func DefaultSteps() map[StepName]Step {
	return DefaultStepsForLifecycle(LifecycleLegacyV2)
}

// DefaultStepsForLifecycle returns noop implementations for one lifecycle schema.
func DefaultStepsForLifecycle(kind LifecycleKind) map[StepName]Step {
	steps := make(map[StepName]Step)
	for _, name := range StepsForLifecycle(kind) {
		steps[name] = &NoopStep{StepName: name}
	}
	return steps
}
