// Package controllogic implements a server-side flow-graph engine. It mirrors
// the client-side panel logic engine (web/src/lib/logic.ts) but runs
// continuously on the server under the root context, independent of any panel.
//
// A control-logic graph is a Node-RED-style flow of trigger, gate, control-flow
// and action nodes connected by wires. Triggers are flow entry points; when one
// activates the engine follows the outgoing wires executing downstream nodes.
//
// Compared with the panel engine, control logic has no button trigger (buttons
// are panel-UI driven) and gains two headless triggers — cron (5-field schedule)
// and alarm (signal out of an allowed range) — plus a Lua script action node
// with get/set/log host functions.
//
// Graphs are persisted as JSON in {storageDir}/controllogic.json and CRUD'd via
// the REST API; the engine rebuilds itself (Reload) whenever they change.
package controllogic

// Node kinds. Triggers begin flows; the rest execute downstream.
//
//	trigger.threshold — fires on the rising edge of cmp(signal, value).
//	trigger.change    — fires whenever a watched signal's value changes.
//	trigger.timer     — fires every `interval` ms.
//	trigger.cron      — fires when the wall clock matches a 5-field cron `spec`.
//	trigger.alarm     — fires on the rising edge of signal leaving [min,max].
//	gate.and          — passes only when all incoming triggers are satisfied.
//	flow.if           — evaluates `cond`, continues on the 'then'/'else' port.
//	flow.loop         — repeats the 'body' port (count / while, capped) then 'done'.
//	action.write      — evaluates `expr`, writes the result to `target`.
//	action.delay      — waits `ms` before continuing.
//	action.log        — logs an expression value to the server log.
//	action.lua        — runs a sandboxed Lua script with get/set/log host funcs.
//	action.dialog     — pushes an info/error/input dialog to connected clients
//	                    filtered by user/group; input responses write `target`.

// Node is a single node in a control-logic graph. Params are stored as strings
// (matching the panel logic model) and parsed per-kind by the engine.
type Node struct {
	ID     string            `json:"id"`
	Kind   string            `json:"kind"`
	X      float64           `json:"x"`
	Y      float64           `json:"y"`
	Params map[string]string `json:"params,omitempty"`
}

// Wire connects an output port of one node to the input of another.
// FromPort defaults to "out" when empty.
type Wire struct {
	From     string `json:"from"`
	FromPort string `json:"fromPort,omitempty"`
	To       string `json:"to"`
}

// NodeGroup is a cosmetic, editor-side grouping of nodes. The engine ignores it;
// it is stored and round-tripped so the editor keeps its visual organisation.
type NodeGroup struct {
	ID        string   `json:"id"`
	Label     string   `json:"label,omitempty"`
	Members   []string `json:"members"`
	Collapsed bool     `json:"collapsed,omitempty"`
}

// Graph is a named, independently-enableable control-logic flow.
type Graph struct {
	ID          string      `json:"id"`
	Name        string      `json:"name"`
	Enabled     bool        `json:"enabled"`
	Version     int         `json:"version,omitempty"`     // git-style revision; bumped on each Save
	Tag         string      `json:"tag,omitempty"`         // optional revision label (e.g. "restored from v3")
	Owner       string      `json:"owner,omitempty"`       // creator identity (stamped server-side)
	Scope       string      `json:"scope,omitempty"`       // access.Scope* visibility token
	ScopeGroups []string    `json:"scopeGroups,omitempty"` // groups for ScopeGroup visibility
	Nodes       []Node      `json:"nodes"`
	Wires       []Wire      `json:"wires"`
	Groups      []NodeGroup `json:"groups,omitempty"`
}

func (n Node) param(key string) string {
	if n.Params == nil {
		return ""
	}
	return n.Params[key]
}
