package confmgr

// WriteFunc writes a resolved value to a target signal. The API layer supplies
// a closure backed by the broker/datasource so confmgr stays decoupled from the
// transport.
type WriteFunc func(ds, signal string, value any) error

// ApplyEntry records the outcome of writing one parameter.
type ApplyEntry struct {
	Key     string `json:"key"`
	DS      string `json:"ds"`
	Signal  string `json:"signal"`
	Value   any    `json:"value,omitempty"`
	OK      bool   `json:"ok"`
	Skipped bool   `json:"skipped,omitempty"`
	Error   string `json:"error,omitempty"`
}

// ApplyResult summarises an apply run.
type ApplyResult struct {
	InstanceID string       `json:"instanceId"`
	SetID      string       `json:"setId"`
	Entries    []ApplyEntry `json:"entries"`
	Applied    int          `json:"applied"`
	Failed     int          `json:"failed"`
	Skipped    int          `json:"skipped"`
}

// Apply writes every resolvable parameter value of an instance to its target
// signal via write. Optional parameters with no value and no default are
// skipped. Individual write failures are recorded per-entry rather than
// aborting the whole apply, so a partial apply is reported faithfully.
func Apply(set ConfigSet, inst ConfigInstance, write WriteFunc) ApplyResult {
	res := ApplyResult{InstanceID: inst.ID, SetID: set.ID, Entries: make([]ApplyEntry, 0, len(set.Parameters))}
	for _, p := range set.Parameters {
		e := ApplyEntry{Key: p.Key, DS: p.DS, Signal: p.Signal}
		v, ok := inst.Resolve(p)
		if !ok {
			e.Skipped = true
			res.Skipped++
			res.Entries = append(res.Entries, e)
			continue
		}
		v = p.normalize(v)
		e.Value = v
		if err := write(p.DS, p.Signal, v); err != nil {
			e.Error = err.Error()
			res.Failed++
		} else {
			e.OK = true
			res.Applied++
		}
		res.Entries = append(res.Entries, e)
	}
	return res
}
