// Package synthetic implements a DataSource that computes signals from
// existing upstream signals by running them through configurable DSP pipelines.
package synthetic

// SignalDef describes one synthetic signal.
type SignalDef struct {
	Name     string       `json:"name"`
	DS       string       `json:"ds"`              // upstream data source name
	Signal   string       `json:"signal"`          // upstream signal name (or "" for constant)
	Inputs   []InputRef   `json:"inputs"`          // alternative multi-input format
	Pipeline []NodeDef    `json:"pipeline"`        // ordered list of DSP nodes
	Graph    *Graph       `json:"graph,omitempty"` // DAG form (preferred when present)
	Meta     MetaOverride `json:"meta"`            // optional metadata overrides

	// Visibility controls who sees this signal in the signal tree:
	//   "global"      — listed in every panel's edit mode
	//   "user"        — listed in every panel owned by Owner
	//   "group"       — listed for Owner and members of any group in Groups
	//   "panel"       — listed only when editing the bound Panel
	// An empty value is treated as "global" for backward compatibility with
	// definitions created before this field existed.
	Visibility string   `json:"visibility,omitempty"`
	Owner      string   `json:"owner,omitempty"`  // creator identity (stamped server-side)
	Groups     []string `json:"groups,omitempty"` // groups for "group" visibility
	Panel      string   `json:"panel,omitempty"`  // bound interface id for "panel" visibility

	// Version and Tag implement git-style revisioning. Version is bumped on
	// every UpdateSignal; superseded revisions are kept as backup files. Tag is
	// an optional human label for a revision (e.g. "restored from v3").
	Version int    `json:"version,omitempty"`
	Tag     string `json:"tag,omitempty"`
}

// InputRef names one upstream signal used as input to the pipeline.
type InputRef struct {
	DS     string `json:"ds"`
	Signal string `json:"signal"`
}

// NodeDef is a single node in the pipeline.
type NodeDef struct {
	Type   string         `json:"type"`
	Params map[string]any `json:"params,omitempty"`
}

// Graph is the DAG form of a synthetic signal: a set of nodes (sources, ops and
// one output) wired together by explicit per-node ordered input lists. It
// supersedes the linear Inputs+Pipeline form: when SignalDef.Graph is set it is
// authoritative; otherwise the legacy linear fields are converted into an
// equivalent graph at load time (see toGraph).
type Graph struct {
	Nodes  []GraphNode `json:"nodes"`
	Output string      `json:"output"` // id of the output node
	// Groups is cosmetic editor metadata (node grouping/collapsing). It has no
	// effect on evaluation; stored and round-tripped so the editor keeps shape.
	Groups []NodeGroup `json:"groups,omitempty"`
}

// NodeGroup is a cosmetic, editor-side grouping of graph nodes (no eval effect).
type NodeGroup struct {
	ID        string   `json:"id"`
	Label     string   `json:"label,omitempty"`
	Members   []string `json:"members"`
	Collapsed bool     `json:"collapsed,omitempty"`
}

// GraphNode is one node in a Graph.
//
//	kind=="source": carries DS+Signal; has no Inputs (a graph root).
//	kind=="op":     carries Op + Params; Inputs lists upstream node IDs in the
//	                order the op receives them (input 0, 1, … e.g. a−b, a÷b).
//	kind=="output": Inputs has a single upstream node whose value is the result.
//
// X/Y are the editor layout coordinates, persisted so a reloaded graph keeps its
// shape; they have no effect on evaluation.
type GraphNode struct {
	ID     string         `json:"id"`
	Kind   string         `json:"kind"`
	Op     string         `json:"op,omitempty"`
	Params map[string]any `json:"params,omitempty"`
	DS     string         `json:"ds,omitempty"`
	Signal string         `json:"signal,omitempty"`
	Inputs []string       `json:"inputs,omitempty"`
	X      float64        `json:"x,omitempty"`
	Y      float64        `json:"y,omitempty"`
}

// MetaOverride allows the synthetic signal to override display metadata.
type MetaOverride struct {
	Unit        string  `json:"unit,omitempty"`
	Description string  `json:"description,omitempty"`
	DisplayLow  float64 `json:"displayLow,omitempty"`
	DisplayHigh float64 `json:"displayHigh,omitempty"`
	Writable    bool    `json:"writable,omitempty"`
}
