/**
 * Automation2 Flow Editor - Data reference format and helpers.
 * All dynamic values use structured ref/value objects, not plain strings.
 */

/** Structured reference to another node's output (path = JSON path segments) */
export interface DataRef {
  type: 'ref';
  nodeId: string;
  path: (string | number)[];
}

/** Explicit static value wrapper */
export interface DataValue {
  type: 'value';
  value: unknown;
}

/** System variable reference */
export interface SystemVarRef {
  type: 'system';
  variable: string;
}

/** Union: reference, static value, or system variable */
export type DynamicValue = DataRef | DataValue | SystemVarRef;

/** Type guards */
export function isSystemVar(v: unknown): v is SystemVarRef {
  return (
    typeof v === 'object' &&
    v !== null &&
    (v as SystemVarRef).type === 'system' &&
    typeof (v as SystemVarRef).variable === 'string'
  );
}

export function isRef(v: unknown): v is DataRef {
  return (
    typeof v === 'object' &&
    v !== null &&
    (v as DataRef).type === 'ref' &&
    typeof (v as DataRef).nodeId === 'string' &&
    Array.isArray((v as DataRef).path)
  );
}

export function isValue(v: unknown): v is DataValue {
  return (
    typeof v === 'object' &&
    v !== null &&
    (v as DataValue).type === 'value'
  );
}

export function isDynamicValue(v: unknown): v is DynamicValue {
  return isRef(v) || isValue(v) || isSystemVar(v);
}

/** Create a system variable reference */
export function createSystemVar(variable: string): SystemVarRef {
  return { type: 'system', variable };
}

/** Create a reference object */
export function createRef(nodeId: string, path: (string | number)[] = []): DataRef {
  return { type: 'ref', nodeId, path };
}

/** Create a value wrapper */
export function createValue(value: unknown): DataValue {
  return { type: 'value', value };
}

/** Resolve a ref against nodeOutputsPreview for UI preview; returns resolved value or undefined if missing */
export function resolvePreview(
  ref: DataRef,
  nodeOutputsPreview: Record<string, unknown>
): unknown {
  const root = nodeOutputsPreview[ref.nodeId];
  if (root === undefined) return undefined;
  let current: unknown = root;
  for (const seg of ref.path) {
    if (current == null) return undefined;
    const key = typeof seg === 'number' ? String(seg) : seg;
    if (Array.isArray(current) && /^\d+$/.test(key)) {
      const idx = parseInt(key, 10);
      if (idx >= 0 && idx < current.length) current = current[idx];
      else return undefined;
    } else if (typeof current === 'object' && key in current) {
      current = (current as Record<string, unknown>)[key];
    } else return undefined;
  }
  return current;
}

/** Format a ref for human display: "Node Title → path.segment" */
export function formatRefLabel(
  ref: DataRef,
  nodes: Array<{ id: string; title?: string }>,
  nodeLabelFallback?: (nodeId: string) => string
): string {
  const node = nodes.find((n) => n.id === ref.nodeId);
  const nodeLabel =
    node?.title?.trim() ||
    nodeLabelFallback?.(ref.nodeId) ||
    ref.nodeId;
  if (ref.path.length === 0) return nodeLabel;
  const pathStr = ref.path.map((p) => String(p)).join(' → ');
  return `${nodeLabel} → ${pathStr}`;
}
