package signedjobs

import (
	"context"
	"encoding/json"
)

// ExecutorChain dispatches a verified signed op to the first sub-executor that OWNS it. A sub-
// executor returns ErrNoExecutor for an op class it does not handle; the chain then tries the next.
// If no sub-executor owns the op, the chain returns ErrNoExecutor so the runner leaves the job
// queued for a later slice (the same contract a single executor has). The chain adds no policy — the
// gate has already verified+bound the op before any Execute runs.
type ExecutorChain []Executor

// Execute implements Executor by trying each sub-executor in order.
func (c ExecutorChain) Execute(ctx context.Context, op string, params json.RawMessage) error {
	for _, e := range c {
		err := e.Execute(ctx, op, params)
		if errorsIs(err, ErrNoExecutor) {
			continue // this sub-executor doesn't own the op — try the next
		}
		return err // owned (handled or failed) — done
	}
	return ErrNoExecutor // no sub-executor owns this op class in this build
}
