function graphToDefinition(nodes, edges, options = {}) {
  const steps = nodes.map((node) => {
    const step = {
      stepId: node.id,
      stepName: node.data.label || node.id,
      stepType: mapNodeTypeToStepType(node.type || "automated")
    };
    if (node.data.description) {
      step.description = node.data.description;
    }
    if (node.data.timeout) {
      step.timeout = node.data.timeout;
    }
    if (node.data.retryPolicy) {
      step.retryPolicy = node.data.retryPolicy;
    }
    if (node.data.config) {
      step.config = node.data.config;
    }
    if (node.type === "userTask" && node.data) {
      step.userTaskConfig = {
        assignedTo: node.data.assignedTo,
        assignedToRoles: node.data.assignedToRoles || [],
        formKey: node.data.formKey,
        allowedActions: node.data.allowedActions || ["complete", "cancel"]
      };
      if (node.data.formSchema || node.data.userTaskConfig?.formSchema) {
        step.userTaskConfig.formSchema = node.data.formSchema || node.data.userTaskConfig.formSchema;
      }
      if (node.data.assignmentRule || node.data.userTaskConfig?.assignmentRule) {
        step.userTaskConfig.assignmentRule = node.data.assignmentRule || node.data.userTaskConfig.assignmentRule;
      }
      if (node.data.slaDuration || node.data.userTaskConfig?.slaDuration) {
        step.userTaskConfig.slaDuration = node.data.slaDuration || node.data.userTaskConfig.slaDuration;
      }
      if (node.data.escalationRules || node.data.userTaskConfig?.escalationRules) {
        step.userTaskConfig.escalationRules = node.data.escalationRules || node.data.userTaskConfig.escalationRules;
      }
    }
    if (node.type === "waitForSignal" && node.data.signalConfig) {
      step.signalConfig = node.data.signalConfig;
    }
    if (node.type === "automated" && node.data.activities) {
      step.activities = node.data.activities;
    }
    if (node.type === "start" && node.data.preConditions && node.data.preConditions.length > 0) {
      step.preConditions = node.data.preConditions;
    }
    if (options.includePositions && node.position) {
      step._editorPosition = {
        x: node.position.x,
        y: node.position.y
      };
    }
    return step;
  });
  const transitions = edges.map((edge) => {
    const edgeData = edge.data;
    const transition = {
      transitionId: edge.id,
      fromStepId: edge.source,
      toStepId: edge.target,
      trigger: edgeData?.trigger || "auto"
    };
    if (edgeData?.transitionName) {
      transition.transitionName = edgeData.transitionName;
    }
    if (edgeData?.priority !== void 0) {
      transition.priority = edgeData.priority;
    }
    if (edgeData?.continueOnActivityFailure !== void 0) {
      transition.continueOnActivityFailure = edgeData.continueOnActivityFailure;
    }
    if (edgeData?.preConditions && edgeData.preConditions.length > 0) {
      transition.preConditions = edgeData.preConditions;
    }
    if (edgeData?.postConditions && edgeData.postConditions.length > 0) {
      transition.postConditions = edgeData.postConditions;
    }
    if (edgeData?.activities && edgeData.activities.length > 0) {
      transition.activities = edgeData.activities.map((activity) => ({
        activityId: activity.activityId,
        activityName: activity.activityName,
        activityType: activity.activityType,
        config: activity.config || {},
        // Include all optional fields
        ...activity.async !== void 0 && { async: activity.async },
        ...activity.timeout && { timeout: activity.timeout },
        ...activity.retryPolicy && { retryPolicy: activity.retryPolicy },
        ...activity.compensate !== void 0 && { compensate: activity.compensate }
      }));
    } else {
      const sourceNode = nodes.find((n) => n.id === edge.source);
      if (sourceNode && sourceNode.type === "automated" && sourceNode.data) {
        if (sourceNode.data.activityType || sourceNode.data.activityId) {
          const activity = {
            activityId: sourceNode.data.activityId || `activity_${sourceNode.id}`,
            activityName: sourceNode.data.activityName || sourceNode.data.label || "Automated Activity",
            activityType: sourceNode.data.activityType || "CALL_API",
            config: sourceNode.data.activityConfig || {}
          };
          if (sourceNode.data.activityAsync !== void 0) {
            activity.async = sourceNode.data.activityAsync;
          }
          if (sourceNode.data.activityTimeout) {
            activity.timeout = sourceNode.data.activityTimeout;
          }
          if (sourceNode.data.activityRetryPolicy) {
            activity.retryPolicy = sourceNode.data.activityRetryPolicy;
          }
          if (sourceNode.data.activityCompensate !== void 0) {
            activity.compensate = sourceNode.data.activityCompensate;
          }
          transition.activities = [activity];
        }
      }
    }
    if (edgeData?.label && !transition.transitionName) {
      transition.transitionName = edgeData.label;
    }
    return transition;
  });
  return {
    steps,
    transitions,
    activities: []
    // Global activities can be added later
  };
}
function definitionToGraph(definition, options = {}) {
  const { autoLayout = true, layoutSpacing = { vertical: 200, horizontal: 300 } } = options;
  const stepMap = new Map(definition.steps.map((step) => [step.stepId, step]));
  const positions = autoLayout ? calculateSmartLayout(definition.steps, definition.transitions, layoutSpacing) : null;
  const nodes = definition.steps.map((step, index) => {
    let position = positions?.get(step.stepId) || { x: 250, y: 50 + index * layoutSpacing.vertical };
    if (!autoLayout && step._editorPosition) {
      position = step._editorPosition;
    }
    const nodeType = mapStepTypeToNodeType(step.stepType);
    const nodeData = {
      label: step.stepName,
      description: step.description,
      stepNumber: index > 0 ? index : void 0
    };
    if (step.timeout) {
      nodeData.timeout = step.timeout;
    }
    if (step.retryPolicy) {
      nodeData.retryPolicy = step.retryPolicy;
    }
    if (step.config) {
      nodeData.config = step.config;
    }
    if (step.stepType === "USER_TASK" && step.userTaskConfig) {
      nodeData.assignedTo = step.userTaskConfig.assignedTo;
      nodeData.assignedToRoles = step.userTaskConfig.assignedToRoles || [];
      nodeData.formKey = step.userTaskConfig.formKey;
      nodeData.allowedActions = step.userTaskConfig.allowedActions;
      nodeData.userTaskConfig = step.userTaskConfig;
      if (step.userTaskConfig.formSchema) {
        nodeData.formSchema = step.userTaskConfig.formSchema;
      }
      if (step.userTaskConfig.assignmentRule) {
        nodeData.assignmentRule = step.userTaskConfig.assignmentRule;
      }
      if (step.userTaskConfig.slaDuration) {
        nodeData.slaDuration = step.userTaskConfig.slaDuration;
      }
      if (step.userTaskConfig.escalationRules) {
        nodeData.escalationRules = step.userTaskConfig.escalationRules;
      }
    }
    if (step.stepType === "WAIT_FOR_SIGNAL" && step.signalConfig) {
      nodeData.signalConfig = step.signalConfig;
    }
    if (step.stepType === "AUTOMATED" && step.activities) {
      nodeData.activities = step.activities;
    }
    if (step.stepType === "START" && step.preConditions) {
      nodeData.preConditions = step.preConditions;
    }
    nodeData.badge = getBadgeForNodeType(nodeType);
    nodeData.status = "pending";
    return {
      id: step.stepId,
      type: nodeType,
      position,
      data: nodeData
    };
  });
  const edges = definition.transitions.map((transition) => {
    return {
      id: transition.transitionId,
      source: transition.fromStepId,
      target: transition.toStepId,
      type: "workflowTransition",
      data: {
        trigger: transition.trigger,
        transitionName: transition.transitionName,
        priority: transition.priority !== void 0 ? transition.priority : 0,
        continueOnActivityFailure: transition.continueOnActivityFailure !== void 0 ? transition.continueOnActivityFailure : false,
        preConditions: transition.preConditions || [],
        postConditions: transition.postConditions || [],
        activities: transition.activities || [],
        label: transition.transitionName || transition.label,
        // Backward compat
        state: transition.state || "pending"
        // Default edge state
      }
    };
  });
  return { nodes, edges };
}
function calculateSmartLayout(steps, transitions, spacing) {
  const positions = /* @__PURE__ */ new Map();
  if (steps.length === 0) return positions;
  const outgoing = /* @__PURE__ */ new Map();
  const incoming = /* @__PURE__ */ new Map();
  for (const step of steps) {
    outgoing.set(step.stepId, []);
    incoming.set(step.stepId, []);
  }
  for (const t of transitions) {
    const children = outgoing.get(t.fromStepId) || [];
    children.push(t.toStepId);
    outgoing.set(t.fromStepId, children);
    const parents = incoming.get(t.toStepId) || [];
    parents.push(t.fromStepId);
    incoming.set(t.toStepId, parents);
  }
  const startNodes = steps.filter((s) => (incoming.get(s.stepId) || []).length === 0);
  if (startNodes.length === 0) {
    startNodes.push(steps[0]);
  }
  const levels = /* @__PURE__ */ new Map();
  const queue = [];
  for (const start of startNodes) {
    queue.push({ id: start.stepId, level: 0 });
  }
  while (queue.length > 0) {
    const { id, level } = queue.shift();
    const currentLevel = levels.get(id);
    if (currentLevel !== void 0 && level <= currentLevel) continue;
    levels.set(id, level);
    if (level > steps.length) continue;
    const children = outgoing.get(id) || [];
    for (const child of children) {
      queue.push({ id: child, level: level + 1 });
    }
  }
  const nodesByLevel = /* @__PURE__ */ new Map();
  for (const [nodeId, level] of levels) {
    const nodesAtLevel = nodesByLevel.get(level) || [];
    nodesAtLevel.push(nodeId);
    nodesByLevel.set(level, nodesAtLevel);
  }
  const centerX = 400;
  const startY = 50;
  for (const [level, nodeIds] of nodesByLevel) {
    const count = nodeIds.length;
    const y = startY + level * spacing.vertical;
    if (count === 1) {
      positions.set(nodeIds[0], { x: centerX, y });
    } else {
      const totalWidth = (count - 1) * spacing.horizontal;
      const startX = centerX - totalWidth / 2;
      nodeIds.sort((a, b) => {
        const parentsA = incoming.get(a) || [];
        const parentsB = incoming.get(b) || [];
        const parentPosA = parentsA.length > 0 ? positions.get(parentsA[0])?.x || 0 : 0;
        const parentPosB = parentsB.length > 0 ? positions.get(parentsB[0])?.x || 0 : 0;
        return parentPosA - parentPosB;
      });
      nodeIds.forEach((nodeId, idx) => {
        positions.set(nodeId, { x: startX + idx * spacing.horizontal, y });
      });
    }
  }
  return positions;
}
function mapNodeTypeToStepType(nodeType) {
  const mapping = {
    start: "START",
    end: "END",
    userTask: "USER_TASK",
    automated: "AUTOMATED",
    decision: "DECISION",
    waitForSignal: "WAIT_FOR_SIGNAL",
    waitForTimer: "WAIT_FOR_TIMER"
  };
  return mapping[nodeType] || "AUTOMATED";
}
function mapStepTypeToNodeType(stepType) {
  const mapping = {
    START: "start",
    END: "end",
    USER_TASK: "userTask",
    AUTOMATED: "automated",
    DECISION: "decision",
    WAIT_FOR_SIGNAL: "waitForSignal",
    WAIT_FOR_TIMER: "waitForTimer"
  };
  return mapping[stepType] || "automated";
}
function getBadgeForNodeType(nodeType) {
  const badges = {
    start: "Start",
    end: "End",
    userTask: "User Task",
    automated: "Automated",
    decision: "Decision",
    waitForSignal: "Wait for Signal",
    waitForTimer: "Wait for Timer"
  };
  return badges[nodeType] || "Task";
}
function validateWorkflowGraph(nodes, edges) {
  const errors = [];
  const startNodes = nodes.filter((n) => n.type === "start");
  if (startNodes.length === 0) {
    errors.push({
      type: "error",
      message: "Workflow must have at least one START node"
    });
  }
  if (startNodes.length > 1) {
    errors.push({
      type: "warning",
      message: "Workflow has multiple START nodes"
    });
  }
  const endNodes = nodes.filter((n) => n.type === "end");
  if (endNodes.length === 0) {
    errors.push({
      type: "error",
      message: "Workflow must have at least one END node"
    });
  }
  for (const node of nodes) {
    if (node.type === "start") continue;
    if (node.type === "end") continue;
    const hasIncoming = edges.some((e) => e.target === node.id);
    const hasOutgoing = edges.some((e) => e.source === node.id);
    if (!hasIncoming && !hasOutgoing) {
      errors.push({
        type: "error",
        message: `Node "${node.data.label}" is disconnected`,
        nodeId: node.id
      });
    } else if (!hasIncoming) {
      errors.push({
        type: "warning",
        message: `Node "${node.data.label}" has no incoming connections`,
        nodeId: node.id
      });
    } else if (!hasOutgoing) {
      errors.push({
        type: "warning",
        message: `Node "${node.data.label}" has no outgoing connections`,
        nodeId: node.id
      });
    }
  }
  const hasCycle = detectCycle(nodes, edges);
  if (hasCycle) {
    errors.push({
      type: "warning",
      message: "Workflow contains cycles (loops)"
    });
  }
  const stepIds = /* @__PURE__ */ new Set();
  for (const node of nodes) {
    if (stepIds.has(node.id)) {
      errors.push({
        type: "error",
        message: `Duplicate step ID: ${node.id}`,
        nodeId: node.id
      });
    }
    stepIds.add(node.id);
  }
  return errors;
}
function detectCycle(nodes, edges) {
  const adjList = /* @__PURE__ */ new Map();
  for (const node of nodes) {
    adjList.set(node.id, []);
  }
  for (const edge of edges) {
    const neighbors = adjList.get(edge.source) || [];
    neighbors.push(edge.target);
    adjList.set(edge.source, neighbors);
  }
  const visited = /* @__PURE__ */ new Set();
  const recStack = /* @__PURE__ */ new Set();
  function dfs(nodeId) {
    visited.add(nodeId);
    recStack.add(nodeId);
    const neighbors = adjList.get(nodeId) || [];
    for (const neighbor of neighbors) {
      if (!visited.has(neighbor)) {
        if (dfs(neighbor)) return true;
      } else if (recStack.has(neighbor)) {
        return true;
      }
    }
    recStack.delete(nodeId);
    return false;
  }
  for (const node of nodes) {
    if (!visited.has(node.id)) {
      if (dfs(node.id)) return true;
    }
  }
  return false;
}
function sanitizeId(input) {
  return input.toLowerCase().replace(/[^a-z0-9_-]/g, "_").replace(/_{2,}/g, "_").replace(/(?:^_|_$)/g, "");
}
function validateId(id) {
  return /^[a-z0-9_-]+$/.test(id);
}
function generateStepId(prefix = "step") {
  const id = `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  return sanitizeId(id);
}
function generateTransitionId(fromStepId, toStepId) {
  const id = `e_${fromStepId}_${toStepId}`;
  return sanitizeId(id);
}
function appendWorkflowEdge(edges, edge) {
  const isDuplicate = edges.some(
    (existing) => existing.source === edge.source && existing.target === edge.target && // Match addEdge: empty-string and nullish handles are equivalent.
    (existing.sourceHandle || null) === (edge.sourceHandle || null) && (existing.targetHandle || null) === (edge.targetHandle || null)
  );
  return isDuplicate ? edges : [...edges, edge];
}
export {
  appendWorkflowEdge,
  definitionToGraph,
  generateStepId,
  generateTransitionId,
  graphToDefinition,
  sanitizeId,
  validateId,
  validateWorkflowGraph
};
//# sourceMappingURL=graph-utils.js.map
