<script setup>
import {
  ref,
  onMounted,
  computed,
  shallowRef,
  watch,
  defineExpose,
  nextTick,
  markRaw,
} from "vue";
import { VueFlow, useVueFlow, Panel } from "@vue-flow/core";
import { Background } from "@vue-flow/background";
import { Controls } from "@vue-flow/controls";
import { MiniMap } from "@vue-flow/minimap";
import InteractiveArrowEdge from "./InteractiveArrowEdge.vue";
import { useNodeValidation } from '~/composables/useNodeValidation';
import { useProcessBuilderStore } from '~/stores/processBuilder';
// Import all file-based custom node components
import StartNode from "~/components/process-flow/custom/StartNode.vue";
import EndNode from "~/components/process-flow/custom/EndNode.vue";
import FormNode from "~/components/process-flow/custom/FormNode.vue";
import ApiNode from "~/components/process-flow/custom/ApiNode.vue";
import GatewayNode from "~/components/process-flow/custom/GatewayNode.vue";
import ScriptNode from "~/components/process-flow/custom/ScriptNode.vue";
import BusinessRuleNode from "~/components/process-flow/custom/BusinessRuleNode.vue";
import NotificationNode from "~/components/process-flow/custom/NotificationNode.vue";
import HtmlNode from "~/components/process-flow/custom/HtmlNode.vue";
import SubprocessNode from "~/components/process-flow/custom/SubprocessNode.vue";
import HexagonShape from "~/components/process-flow/custom/HexagonShape.vue";
import TrapezoidShape from "~/components/process-flow/custom/TrapezoidShape.vue";
import RectangleShape from "~/components/process-flow/custom/RectangleShape.vue";
import SwimlaneHorizontal from "~/components/process-flow/custom/SwimlaneHorizontal.vue";
import SwimlaneVertical from "~/components/process-flow/custom/SwimlaneVertical.vue";
import TextAnnotation from "~/components/process-flow/custom/TextAnnotation.vue";
import ProcessGroup from "~/components/process-flow/custom/ProcessGroup.vue";
import "@vue-flow/core/dist/style.css";
import "@vue-flow/core/dist/theme-default.css";
import "@vue-flow/controls/dist/style.css";
import "@vue-flow/minimap/dist/style.css";

// Create nodeTypes object with markRaw to prevent reactivity issues
const customNodeTypes = {
  start: markRaw(StartNode),
  end: markRaw(EndNode),
  form: markRaw(FormNode),
  api: markRaw(ApiNode),
  gateway: markRaw(GatewayNode),
  script: markRaw(ScriptNode),
  "business-rule": markRaw(BusinessRuleNode),
  notification: markRaw(NotificationNode),
  html: markRaw(HtmlNode),
  subprocess: markRaw(SubprocessNode),
  // Shape nodes
  "hexagon-shape": markRaw(HexagonShape),
  "trapezoid-shape": markRaw(TrapezoidShape),
  "rectangle-shape": markRaw(RectangleShape),
  "swimlane-horizontal": markRaw(SwimlaneHorizontal),
  "swimlane-vertical": markRaw(SwimlaneVertical),
  "text-annotation": markRaw(TextAnnotation),
  "process-group": markRaw(ProcessGroup),
};

// Add Material Icons import
const materialIconsLink = document.createElement("link");
materialIconsLink.href =
  "https://fonts.googleapis.com/icon?family=Material+Icons";
materialIconsLink.rel = "stylesheet";
document.head.appendChild(materialIconsLink);

const props = defineProps({
  initialNodes: {
    type: Array,
    default: () => [],
  },
  initialEdges: {
    type: Array,
    default: () => [],
  },
  highlightedNodeId: {
    type: String,
    default: null,
  },
});

const emit = defineEmits([
  "nodeClick",
  "edgeClick",
  "paneClick",
  "nodesChange",
  "edgesChange",
  "nodeSelected",
  "edgeSelected",
  "selectionChange",
  "nodesSelection",
]);

// Get the flow instance
const { flowInstance } = useVueFlow();

// Initialize validation system
const {
  validateProcess,
  validationResults,
  isValidating,
  overallValidationStatus,
  validationSummary,
  clearValidation
} = useNodeValidation();

// Initialize process store for validation updates
const processStore = useProcessBuilderStore();

// Initialize Vue Flow
const {
  nodes,
  edges,
  addNodes,
  addEdges,
  onNodesChange,
  onEdgesChange,
  onConnect,
  fitView,
  fitBounds,
  project,
  removeNodes,
  removeEdges,
  updateNodeInternals,
  setViewport,
  getViewport,
} = useVueFlow({
  defaultEdgeOptions: {
    animated: true,
    type: "custom",
    style: {
      stroke: "#555",
      strokeWidth: 2,
    },
    markerEnd: "url(#arrow)",
  },
  deleteKeyCode: "Delete",
  connectionMode: "strict",
  edgeUpdaterRadius: 12,
  edgesUpdatable: true,
  defaultMarkerColor: "#555",
  isValidConnection: (connection) => {
    return true;
  },
  selectNodesOnDrag: true,
  panOnDrag: [1, 2],
  panOnScroll: false,
  zoomOnScroll: true,
});

// Define custom edge types - use markRaw to prevent reactivity issues
const edgeTypes = {
  custom: shallowRef(InteractiveArrowEdge),
};

// Default nodes if empty
const defaultNodes = [];

// Default edges if empty
const defaultEdges = [];

// Flow configuration
const flowOptions = ref({
  defaultZoom: 1,
  minZoom: 0.2,
  maxZoom: 4,
  fitViewOnInit: true,
  snapToGrid: true,
  snapGrid: [15, 15],
  edgeUpdaterRadius: 10,
  connectionMode: "strict",
  connectionRadius: 25,
  elevateEdgesOnSelect: true,
  nodesDraggable: true,
  nodesConnectable: true,
  elementsSelectable: true,
  selectNodesOnDrag: true,
  panOnDrag: [1, 2],
  panOnScroll: false,
  zoomOnScroll: true,
  zoomOnPinch: true,
  zoomOnDoubleClick: false,
  connectOnClick: false,
  selectionMode: "partial",
  selectionKeyCode: true,
  multiSelectionKeyCode: true,
  selectionOnDrag: true,
  selectionBoxMode: "Default",
  preventScrolling: true,
});

// Use shallowRef for selected node to avoid unnecessary reactivity
const selectedNode = shallowRef(null);

// Help guide state
const showHelpGuide = ref(false);

// Toggle help guide
const toggleHelpGuide = () => {
  showHelpGuide.value = !showHelpGuide.value;
};

// Validation panel state
const showValidationPanel = ref(true);

// Toggle validation panel
const toggleValidationPanel = () => {
  showValidationPanel.value = !showValidationPanel.value;
};

// State management for preventing recursive updates
const isUpdatingNodes = ref(false);
const isUpdatingEdges = ref(false);

// Handle node selection
const onNodeClick = ({ node }) => {
  // Check if node exists and has required properties
  if (!node || !node.id) {
    console.warn("Invalid node clicked - Missing required properties");
    return;
  }

  try {
    // Create a plain object copy of the node to avoid reactivity issues
    const nodeData = {
      id: node.id,
      type: node.type,
      label: node.label || (node.data && node.data.label) || "",
      data: node.data ? JSON.parse(JSON.stringify(node.data)) : {},
      position: node.dimensions
        ? {
            x: node.dimensions.x || 0,
            y: node.dimensions.y || 0,
          }
        : { x: 0, y: 0 },
    };

    // console.log('Processed node data:', nodeData);
    selectedNode.value = nodeData;
    emit("nodeSelected", nodeData);
  } catch (error) {
    console.error("Error processing node click:", error);
  }
};

// Handle selection change
const onSelectionChange = ({ nodes, edges }) => {
  console.log('Selection changed:', { nodes: nodes.length, edges: edges.length });
  // You can add custom logic here for handling multiple selections
};

// Handle multiple node selection
const onNodesSelection = ({ nodes }) => {
  console.log('Multiple nodes selected:', nodes.map(n => n.id));
  // Emit event for multiple node selection
  emit("nodeSelected", nodes);
};

// Handle edge click
const onEdgeClick = (event, edge) => {
  // Handle different parameter formats Vue Flow might send
  let actualEdge = edge;

  // Sometimes Vue Flow passes the edge in the event object
  if (!actualEdge && event && typeof event === "object") {
    if (event.edge) {
      actualEdge = event.edge;
    } else if (event.id) {
      // The event itself might be the edge
      actualEdge = event;
    }
  }

  // Add comprehensive null check for edge
  if (!actualEdge || !actualEdge.id) {
    console.warn("Invalid edge data received in onEdgeClick:", {
      event,
      edge,
      actualEdge,
    });
    return;
  }

  // Create a simplified copy of the edge data
  const edgeData = {
    id: actualEdge.id,
    source: actualEdge.source,
    target: actualEdge.target,
    label: actualEdge.label || "",
    sourceNode: nodes.value.find((node) => node.id === actualEdge.source),
    targetNode: nodes.value.find((node) => node.id === actualEdge.target),
  };

  emit("edgeSelected", edgeData);
  emit("edgeClick", actualEdge);
};

// Handle pane (background) click
const onPaneClick = () => {
  selectedNode.value = null;
  emit("paneClick");
};

// Window resize handler
const resizeFlow = () => {
  setTimeout(() => {
    fitView();
  }, 200);
};

onMounted(() => {
  // Initialize with provided nodes or default ones
  if (props.initialNodes.length) {
    console.log("Mounted: initialNodes", props.initialNodes);
    addNodes(props.initialNodes);
  } else {
    addNodes(defaultNodes);
  }

  // Initialize with provided edges or default ones
  if (props.initialEdges.length) {
    addEdges(props.initialEdges);
  } else {
    addEdges(defaultEdges);
  }

  // Setup window resize handler
  window.addEventListener("resize", resizeFlow);

  // FIXED: Only fit view initially if there are no existing nodes OR if nodes have no positions
  // This prevents repositioning existing nodes when the canvas mounts
  const hasExistingNodes = props.initialNodes.length > 0;
  const hasPositions = props.initialNodes.some(
    (node) => node.position && (node.position.x !== 0 || node.position.y !== 0)
  );

  // Only auto-fit if we have no nodes, or nodes have default/zero positions
  if (!hasExistingNodes || !hasPositions) {
    setTimeout(() => {
      fitView();
    }, 100);
  }
});

// Center on a specific node
const centerOnNode = (nodeId, nodePosition) => {
  if (!nodePosition) {
    // Try to find the node position from current nodes
    const targetNode = nodes.value.find((n) => n.id === nodeId);
    if (targetNode) {
      nodePosition = targetNode.position;
    } else {
      console.warn(
        "Could not find node to highlight:",
        nodeId,
        "Available nodes:",
        nodes.value.map((n) => n.id)
      );
      return;
    }
  }

  try {
    // Use the already available setViewport from useVueFlow
    // Calculate the center position
    // Account for the container size and panel offsets
    const containerRect = document
      .querySelector(".vue-flow")
      ?.getBoundingClientRect();
    const centerX = containerRect ? containerRect.width / 2 : 400;
    const centerY = containerRect ? containerRect.height / 2 : 300;

    // Calculate new viewport to center the node
    const newViewport = {
      x: centerX - nodePosition.x - 100, // Offset for left panel
      y: centerY - nodePosition.y,
      zoom: 1.2,
    };

    setViewport(newViewport, { duration: 800 });
  } catch (error) {
    console.error("Error in centerOnNode:", error);
  }
};

// The centerOnNode method will be exposed along with other methods below

// Watch for changes to highlightedNodeId and apply highlighting
watch(
  () => props.highlightedNodeId,
  (newHighlightedId, oldHighlightedId) => {
    // Remove highlight from previously highlighted node
    if (oldHighlightedId) {
      const oldNode = nodes.value.find((node) => node.id === oldHighlightedId);
      if (oldNode) {
        // Use node data approach for highlighting
        if (oldNode.data) {
          oldNode.data.highlighted = false;
        }
        // Also try class approach
        if (oldNode.class) {
          oldNode.class = oldNode.class.replace(" highlighted-node", "").trim();
        }

        // Try to find and modify the actual DOM element
        nextTick(() => {
          const nodeElement = document.querySelector(
            `[data-id="${oldHighlightedId}"]`
          );
          if (nodeElement) {
            nodeElement.classList.remove("highlighted-node");
            nodeElement.style.removeProperty("box-shadow");
            nodeElement.style.removeProperty("z-index");
            nodeElement.style.removeProperty("transition");

            const customNode = nodeElement.querySelector(".custom-node");
            if (customNode) {
              customNode.classList.remove("highlighted-node");
              customNode.style.removeProperty("box-shadow");
              customNode.style.removeProperty("z-index");
              customNode.style.removeProperty("transition");
            }
          }
        });
      }
    }

    // Add highlight to new highlighted node
    if (newHighlightedId) {
      const newNode = nodes.value.find((node) => node.id === newHighlightedId);
      if (newNode) {
        // Use node data approach for highlighting
        if (!newNode.data) {
          newNode.data = {};
        }
        newNode.data.highlighted = true;

        // Also try class approach
        if (!newNode.class?.includes("highlighted-node")) {
          newNode.class = (newNode.class || "").trim() + " highlighted-node";
        }

        // Try to find and modify the actual DOM element directly with inline styles
        nextTick(() => {
          const nodeElement = document.querySelector(
            `[data-id="${newHighlightedId}"]`
          );
          if (nodeElement) {
            nodeElement.classList.add("highlighted-node");
            nodeElement.setAttribute("data-highlighted", "true");

            // Apply subtle inline styles for immediate visual effect
            nodeElement.style.boxShadow =
              "0 0 0 2px #3b82f6, 0 4px 12px rgba(59, 130, 246, 0.3)";
            nodeElement.style.zIndex = "999";
            nodeElement.style.transition = "all 0.3s ease-in-out";

            const customNode = nodeElement.querySelector(".custom-node");
            if (customNode) {
              customNode.classList.add("highlighted-node");
              customNode.setAttribute("data-highlighted", "true");

              // Apply subtle styles to custom node as well
              customNode.style.boxShadow =
                "0 0 0 2px #3b82f6, 0 4px 12px rgba(59, 130, 246, 0.3)";
              customNode.style.zIndex = "999";
              customNode.style.transition = "all 0.3s ease-in-out";
            }
          } else {
            console.warn(
              "Could not find DOM element for node:",
              newHighlightedId
            );
          }
        });

        // Force node update
        updateNodeInternals([newHighlightedId]);
      } else {
        console.warn(
          "Could not find node to highlight:",
          newHighlightedId,
          "Available nodes:",
          nodes.value.map((n) => ({ id: n.id, type: n.type }))
        );
      }
    }
  }
);

// Watch for changes to initialNodes prop and update the canvas
watch(
  () => props.initialNodes,
  async (newNodes, oldNodes) => {
    console.log("Watcher: initialNodes changed", newNodes);

    if (newNodes && Array.isArray(newNodes) && !isUpdatingNodes.value) {
      isUpdatingNodes.value = true;

      try {
        // Instead of clearing all nodes, sync them intelligently
        const currentNodeIds = new Set(nodes.value.map((n) => n.id));
        const newNodeIds = new Set(newNodes.map((n) => n.id));

        // Remove nodes that are no longer in the new list
        const nodesToRemove = nodes.value.filter(
          (node) => !newNodeIds.has(node.id)
        );
        if (nodesToRemove.length > 0) {
          removeNodes(nodesToRemove);
        }

        // Add new nodes that aren't already present
        const nodesToAdd = newNodes.filter(
          (node) => !currentNodeIds.has(node.id)
        );
        if (nodesToAdd.length > 0) {
          addNodes([...nodesToAdd]); // Create a copy to avoid reactivity issues
        }

        // Update existing nodes that have changed - STRICTLY PRESERVE CANVAS POSITIONS
        newNodes.forEach((newNode) => {
          const existingNode = nodes.value.find((n) => n.id === newNode.id);
          if (existingNode) {
            // FIXED: NEVER update positions for existing nodes during sync
            // The canvas is the source of truth for positions once a node exists
            // Only update data and label, preserve the current canvas position
            Object.assign(existingNode, {
              label: newNode.label,
              data: { ...newNode.data },
              // ALWAYS keep the current canvas position - never overwrite from store
              position: existingNode.position,
            });

            // Always call updateNodeInternals to force re-render
            // This ensures property changes are reflected immediately
            updateNodeInternals([newNode.id]);
          }
        });

        // FIXED: Don't auto-fit view when adding individual nodes to prevent repositioning
        // Only fit view if this is a bulk operation (like loading a process) with many nodes
        if (nodesToAdd.length > 3) {
          await nextTick();
          setTimeout(() => {
            fitView();
          }, 100);
        }
      } finally {
        // Use a small delay to prevent immediate re-triggering
        setTimeout(() => {
          isUpdatingNodes.value = false;
        }, 50);
      }
    }
  },
  { deep: true }
);

// Watch for changes to initialEdges prop and update the canvas
// This watcher depends on nodes being already present
watch(
  () => [props.initialEdges, nodes.value.length],
  async ([newEdges, nodeCount]) => {
    if (
      newEdges &&
      Array.isArray(newEdges) &&
      nodeCount > 0 &&
      !isUpdatingEdges.value
    ) {
      isUpdatingEdges.value = true;

      try {
        // Instead of clearing all edges, sync them intelligently
        const currentEdgeIds = new Set(edges.value.map((e) => e.id));
        const newEdgeIds = new Set(newEdges.map((e) => e.id));

        // CRITICAL: Be more conservative about edge removal
        // Only remove edges that are definitely not in the new list AND whose nodes don't exist
        const edgesToRemove = edges.value.filter((edge) => {
          const isInNewList = newEdgeIds.has(edge.id);

          if (isInNewList) {
            return false; // Don't remove if it's in the new list
          }

          // Double-check that both source and target nodes still exist
          const sourceExists = nodes.value.some(
            (node) => node.id === edge.source
          );
          const targetExists = nodes.value.some(
            (node) => node.id === edge.target
          );

          // Only remove if nodes don't exist (orphaned edges)
          const shouldRemove = !sourceExists || !targetExists;

          if (shouldRemove) {
            console.log(
              `🗑️ Removing orphaned edge ${edge.id}: source ${edge.source} exists: ${sourceExists}, target ${edge.target} exists: ${targetExists}`
            );
          }

          return shouldRemove;
        });

        if (edgesToRemove.length > 0) {
          console.log(
            "🗑️ Removing edges:",
            edgesToRemove.map((e) => `${e.source}->${e.target} (${e.id})`)
          );
          removeEdges(edgesToRemove);
        }

        if (newEdges.length > 0) {
          // Verify all nodes exist before adding edges
          const validEdges = newEdges.filter((edge) => {
            const sourceExists = nodes.value.some(
              (node) => node.id === edge.source
            );
            const targetExists = nodes.value.some(
              (node) => node.id === edge.target
            );

            if (!sourceExists || !targetExists) {
              console.warn(
                `⚠️ Skipping edge ${edge.id}: source ${edge.source} exists: ${sourceExists}, target ${edge.target} exists: ${targetExists}`
              );
              return false;
            }

            return true;
          });

          // Add new edges that aren't already present
          const edgesToAdd = validEdges.filter(
            (edge) => !currentEdgeIds.has(edge.id)
          );

          if (edgesToAdd.length > 0) {
            console.log(
              "➕ Adding new edges:",
              edgesToAdd.map((e) => `${e.source}->${e.target} (${e.id})`)
            );

            // Ensure all edges have proper handle specifications
            const edgesWithHandles = edgesToAdd.map((edge) => {
              // IMPORTANT: If edge already has sourceHandle and targetHandle, preserve them exactly as they are
              if (edge.sourceHandle && edge.targetHandle) {
                console.log(
                  `🔗 Edge ${edge.id} already has handles: ${edge.sourceHandle} -> ${edge.targetHandle}`
                );
                return edge;
              }

              // Otherwise, generate default handles based on node types
              const sourceNode = nodes.value.find((n) => n.id === edge.source);
              const targetNode = nodes.value.find((n) => n.id === edge.target);

              let sourceHandle = edge.sourceHandle;
              let targetHandle = edge.targetHandle;

              // Generate default source handle if missing
              if (!sourceHandle && sourceNode) {
                if (sourceNode.type === "start") {
                  sourceHandle = `${edge.source}-bottom`; // Start nodes prefer bottom output
                } else if (sourceNode.type === "gateway") {
                  sourceHandle = `${edge.source}-right`; // Gateway nodes prefer right output for first connection
                } else {
                  sourceHandle = `${edge.source}-bottom`; // Most nodes prefer bottom output
                }
              }

              // Generate default target handle if missing
              if (!targetHandle && targetNode) {
                if (targetNode.type === "end") {
                  targetHandle = `${edge.target}-top`; // End nodes prefer top input
                } else {
                  targetHandle = `${edge.target}-top`; // Most nodes prefer top input
                }
              }

              console.log(
                `🔗 Generated handles for edge ${edge.id}: ${sourceHandle} -> ${targetHandle}`
              );
              return {
                ...edge,
                sourceHandle,
                targetHandle,
              };
            });

            addEdges([...edgesWithHandles]); // Create a copy to avoid reactivity issues
          }

          // Update existing edges that have changed - IMPORTANT: preserve handle positions
          let updatedEdgeCount = 0;
          newEdges.forEach((newEdge) => {
            const existingEdge = edges.value.find((e) => e.id === newEdge.id);
            if (existingEdge) {
              // Check if the edge has actually changed before updating
              const hasChanges =
                existingEdge.label !== newEdge.label ||
                JSON.stringify(existingEdge.style) !==
                  JSON.stringify(newEdge.style);

              if (hasChanges) {
                Object.assign(existingEdge, {
                  label: newEdge.label,
                  // CRITICAL: Preserve existing handles if they exist
                  sourceHandle:
                    existingEdge.sourceHandle || newEdge.sourceHandle,
                  targetHandle:
                    existingEdge.targetHandle || newEdge.targetHandle,
                  style: newEdge.style ? { ...newEdge.style } : undefined,
                });
                updatedEdgeCount++;
              }
            }
          });

          if (updatedEdgeCount > 0) {
            console.log("🔄 Updated existing edges:", updatedEdgeCount);
          }
        }
      } finally {
        // Use a small delay to prevent immediate re-triggering
        setTimeout(() => {
          isUpdatingEdges.value = false;
        }, 50);
      }
    } else if (
      newEdges &&
      Array.isArray(newEdges) &&
      newEdges.length > 0 &&
      nodeCount === 0
    ) {
      console.log(
        "⚠️ ProcessFlowCanvas: Edges provided but no nodes yet, waiting..."
      );
    }
  },
  { deep: true }
);

// Handle node changes
onNodesChange((changes) => {
  emit("nodesChange", changes, nodes.value);
});

// Handle edge changes
onEdgesChange((changes) => {
  emit("edgesChange", changes, edges.value);
});

// Watch for changes to nodes and edges to trigger validation
watch(
  () => [nodes.value, edges.value],
  ([currentNodes, currentEdges]) => {
    if (currentNodes && currentNodes.length > 0) {
      // Debounce validation to avoid excessive re-computation
      clearTimeout(validationTimeout.value);
      validationTimeout.value = setTimeout(() => {
        const issues = validateProcess(currentNodes, currentEdges || []);
        
        // Update the process store with validation results
        processStore.updateValidationResults(
          validationResults.value,
          validationSummary.value,
          overallValidationStatus.value
        );
      }, 300);
    } else {
      // Clear validation if no nodes
      clearValidation();
      processStore.clearValidationResults();
    }
  },
  { deep: true }
);

// Timeout for debouncing validation
const validationTimeout = ref(null);

// Handle new connections
const handleConnect = (connection) => {
  if (!connection.source || !connection.target) return;

  // Try to determine if this is coming from a gateway
  const sourceNode = nodes.value.find((node) => node.id === connection.source);
  let label = "";

  // If the source is a gateway, we should add a label based on conditions
  if (sourceNode && sourceNode.type === "gateway") {
    // Check if there's a default path label
    if (sourceNode.data && sourceNode.data.defaultPath) {
      label = sourceNode.data.defaultPath;
    }

    // For existing gateway connections, check if we should use a condition's output
    const existingEdges = edges.value.filter(
      (edge) => edge.source === connection.source
    );
    if (
      existingEdges.length === 0 &&
      sourceNode.data.conditions &&
      sourceNode.data.conditions.length > 0
    ) {
      // If this is the first connection and we have conditions, use the first condition's output
      const firstCondition = sourceNode.data.conditions[0];
      if (firstCondition && firstCondition.output) {
        label = firstCondition.output;
      }
    } else if (sourceNode.data.conditions) {
      // If we already have connections, try to find an unused condition
      const usedOutputs = existingEdges.map((edge) => edge.label);
      const unusedCondition = sourceNode.data.conditions.find(
        (condition) =>
          condition.output && !usedOutputs.includes(condition.output)
      );

      if (unusedCondition) {
        label = unusedCondition.output;
      }
    }
  }

  const newEdge = {
    id: `${connection.source}-${connection.target}-${Date.now()}`,
    source: connection.source,
    target: connection.target,
    sourceHandle: connection.sourceHandle,
    targetHandle: connection.targetHandle,
    type: "custom",
    animated: true,
    style: {
      stroke: "#555",
      strokeWidth: 2,
    },
    markerEnd: "url(#arrow)",
    label: label,
  };

  // Add the edge directly to Vue Flow for immediate visual feedback
  addEdges([newEdge]);

  // Emit the edge change in the format the parent expects
  emit("edgesChange", [{ type: "add", id: newEdge.id }], edges.value);
};

// Handle node removal
const onNodeDelete = (event) => {
  // Check if we have a node in the event
  if (event && event.node) {
    removeNodes([event.node]);
    emit("nodesChange", nodes.value);
  }
};

// Handle edge removal
const onEdgeDelete = (event) => {
  // Check if we have an edge in the event
  if (event && event.edge) {
    removeEdges([event.edge]);
    emit("edgesChange", edges.value);
  }
};

// Handle delete key press to remove selected elements
const onDeleteKeyPress = () => {
  const { getSelectedNodes, getSelectedEdges } = flowInstance.value;
  const selectedNodes = getSelectedNodes();
  const selectedEdges = getSelectedEdges();

  if (selectedNodes.length > 0) {
    removeNodes(selectedNodes);
    emit("nodesChange", nodes.value);
  }

  if (selectedEdges.length > 0) {
    removeEdges(selectedEdges);
    emit("edgesChange", edges.value);
  }
};

// Handle drop event
const onDrop = (event) => {
  event.preventDefault();
  event.stopPropagation();

  try {
    // Get the dragged component data
    const componentData = JSON.parse(event.dataTransfer.getData("text/plain"));
    if (!componentData) return;

    // Get the Vue Flow wrapper element
    const flowWrapper = event.currentTarget;
    const bounds = flowWrapper.getBoundingClientRect();

    // Calculate the position relative to the wrapper
    const position = project({
      x: event.clientX - bounds.left,
      y: event.clientY - bounds.top,
    });

    // Create new node (both process nodes and shapes are treated as nodes in Vue Flow)
    const newNode = {
      id: `${componentData.type}-${Date.now()}`,
      type: componentData.type,
      position,
      label: componentData.label,
      data: {
        ...componentData.data,
        label: componentData.label,
      },
    };

    // For shape nodes, ensure they don't have connection handles
    if (componentData.data?.isShape) {
      // Shapes should not be connectable
      newNode.connectable = false;
    }

    // FIXED: Only emit to parent, don't add directly to canvas
    // The parent will add to store, which will trigger canvas update through watchers
    // This prevents double node creation (canvas + store)
    emit(
      "nodesChange",
      [{ type: "add", id: newNode.id, item: newNode }],
      nodes.value
    );
    
    console.log('📦 Canvas: Drop handled, emitted to parent:', newNode.id);
  } catch (error) {
    console.error("Error handling drop:", error);
  }
};

// Handle drag over
const onDragOver = (event) => {
  event.preventDefault();
  event.stopPropagation();
  event.dataTransfer.dropEffect = "copy";
};

// Export functions for canvas export
async function exportToPNG(options = {}) {
  try {
    const { default: html2canvas } = await import('html2canvas');
    
    // Find Vue Flow elements
    const vueFlowContainer = document.querySelector('.vue-flow');
    const vueFlowViewport = document.querySelector('.vue-flow__viewport');
    
    if (!vueFlowContainer || !vueFlowViewport) {
      throw new Error('Vue Flow elements not found');
    }

    console.log('Vue Flow elements found:', { container: vueFlowContainer, viewport: vueFlowViewport });

    // Wait for any animations or transitions to complete
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Get current nodes from Vue Flow state
    const currentNodes = nodes.value || [];
    
    console.log('Current nodes for export:', currentNodes.length);

    if (currentNodes.length === 0) {
      throw new Error('No nodes found to export');
    }

    // Store original viewport
    const originalViewport = getViewport();
    
    // Use Vue Flow's fitView to ensure all nodes are visible and centered
    fitView({ 
      padding: 0.2, 
      includeHiddenNodes: false,
      minZoom: 0.5,
      maxZoom: 2
    });
    
    // Wait for the view to settle
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Use html2canvas to capture the entire Vue Flow container
    const html2canvasOptions = {
      backgroundColor: options.includeBackground ? '#f8fafc' : 'white',
      scale: options.quality || 2,
      useCORS: true,
      allowTaint: false,
      foreignObjectRendering: true,
      imageTimeout: 20000,
      logging: false,
      removeContainer: false,
      // Capture the entire container instead of just viewport
      width: vueFlowContainer.offsetWidth,
      height: vueFlowContainer.offsetHeight,
      x: 0,
      y: 0,
      scrollX: 0,
      scrollY: 0,
      windowWidth: vueFlowContainer.offsetWidth,
      windowHeight: vueFlowContainer.offsetHeight,
      onclone: function(clonedDoc) {
        const clonedContainer = clonedDoc.querySelector('.vue-flow');
        const clonedViewport = clonedDoc.querySelector('.vue-flow__viewport');
        
        if (clonedContainer && clonedViewport) {
          // Ensure container styles don't interfere with capture
          clonedContainer.style.overflow = 'visible';
          clonedContainer.style.position = 'relative';
          clonedViewport.style.overflow = 'visible';
          
          // Make sure all content is visible in the cloned document
          const allElements = clonedContainer.querySelectorAll('*');
          allElements.forEach(el => {
            // Force visibility of all elements
            if (el.style.display === 'none') {
              el.style.display = 'block';
            }
            if (el.style.visibility === 'hidden') {
              el.style.visibility = 'visible';
            }
            if (el.style.opacity === '0' || el.style.opacity === '') {
              el.style.opacity = '1';
            }
            // Remove any transforms that might hide content
            if (el.style.transform && el.style.transform.includes('scale(0)')) {
              el.style.transform = el.style.transform.replace(/scale\([0-9.]*\)/, 'scale(1)');
            }
          });
          
          console.log('Cloned document prepared, elements made visible');
        }
      },
      ignoreElements: function(element) {
        // Only ignore UI controls, not the actual process content
        return element.classList.contains('vue-flow__controls') ||
               element.classList.contains('vue-flow__minimap') ||
               (element.classList.contains('vue-flow__background') && !options.includeBackground);
      }
    };

    console.log('html2canvas options:', html2canvasOptions);

    // Capture the Vue Flow container
    const canvas = await html2canvas(vueFlowContainer, html2canvasOptions);

    console.log('Canvas captured successfully:', canvas.width, 'x', canvas.height);

    // Restore original viewport
    setViewport(originalViewport);

    // Convert to blob and download
    return new Promise((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (!blob) {
          reject(new Error('Failed to create image blob'));
          return;
        }
        
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = options.fileName || 'process-flow.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
        resolve();
      }, 'image/png', 0.95);
    });
  } catch (error) {
    console.error('Error exporting to PNG:', error);
    throw error;
  }
}

async function exportToPDF(options = {}) {
  try {
    // Use the same improved capture logic as PNG export
    const { default: html2canvas } = await import('html2canvas');
    
    const vueFlowContainer = document.querySelector('.vue-flow');
    const vueFlowViewport = document.querySelector('.vue-flow__viewport');
    
    if (!vueFlowContainer || !vueFlowViewport) {
      throw new Error('Vue Flow elements not found');
    }

    console.log('Vue Flow elements found for PDF:', { container: vueFlowContainer, viewport: vueFlowViewport });

    // Wait for any animations or transitions to complete
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Get current nodes from Vue Flow state
    const currentNodes = nodes.value || [];
    
    console.log('Current nodes for PDF export:', currentNodes.length);

    if (currentNodes.length === 0) {
      throw new Error('No nodes found to export');
    }

    // Store original viewport
    const originalViewport = getViewport();
    
    // Use Vue Flow's fitView to ensure all nodes are visible and centered
    fitView({ 
      padding: 0.2, 
      includeHiddenNodes: false,
      minZoom: 0.5,
      maxZoom: 2
    });
    
    // Wait for the view to settle
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Use html2canvas to capture the entire viewport
    const html2canvasOptions = {
      backgroundColor: options.includeBackground ? '#f8fafc' : '#ffffff',
      scale: options.quality || 2,
      useCORS: true,
      allowTaint: false,
      foreignObjectRendering: true,
      imageTimeout: 20000,
      logging: false,
      removeContainer: false,
      // Capture the entire viewport
      width: vueFlowViewport.offsetWidth,
      height: vueFlowViewport.offsetHeight,
      x: 0,
      y: 0,
      scrollX: 0,
      scrollY: 0,
      windowWidth: vueFlowViewport.offsetWidth,
      windowHeight: vueFlowViewport.offsetHeight,
      onclone: function(clonedDoc) {
        const clonedContainer = clonedDoc.querySelector('.vue-flow');
        const clonedViewport = clonedDoc.querySelector('.vue-flow__viewport');
        
        if (clonedContainer && clonedViewport) {
          // Ensure container styles don't interfere with capture
          clonedContainer.style.overflow = 'visible';
          clonedContainer.style.position = 'relative';
          clonedViewport.style.overflow = 'visible';
          
          // Make sure all content is visible in the cloned document
          const allElements = clonedContainer.querySelectorAll('*');
          allElements.forEach(el => {
            // Force visibility of all elements
            if (el.style.display === 'none') {
              el.style.display = 'block';
            }
            if (el.style.visibility === 'hidden') {
              el.style.visibility = 'visible';
            }
            if (el.style.opacity === '0' || el.style.opacity === '') {
              el.style.opacity = '1';
            }
            // Remove any transforms that might hide content
            if (el.style.transform && el.style.transform.includes('scale(0)')) {
              el.style.transform = el.style.transform.replace(/scale\([0-9.]*\)/, 'scale(1)');
            }
          });
        }
      },
      ignoreElements: function(element) {
        // Only ignore UI controls, not the actual process content
        return element.classList.contains('vue-flow__controls') ||
               element.classList.contains('vue-flow__minimap') ||
               (element.classList.contains('vue-flow__background') && !options.includeBackground);
      }
    };

    // Capture the Vue Flow container
    const canvas = await html2canvas(vueFlowContainer, html2canvasOptions);
    
    // Restore original viewport
    setViewport(originalViewport);
    
    // Now create PDF
    const { jsPDF } = await import('jspdf');
    const imgData = canvas.toDataURL('image/png');

    console.log('PDF Canvas captured:', canvas.width, 'x', canvas.height);

    // Calculate dimensions
    const imgWidth = canvas.width;
    const imgHeight = canvas.height;

    // PDF page configurations
    const pageConfigs = {
      a4: { width: 210, height: 297 },
      a3: { width: 297, height: 420 },
      letter: { width: 216, height: 279 },
      legal: { width: 216, height: 356 }
    };

    let pageConfig = pageConfigs[options.pdf?.pageSize] || pageConfigs.a4;
    const orientation = options.pdf?.orientation || 'landscape';

    if (orientation === 'landscape') {
      [pageConfig.width, pageConfig.height] = [pageConfig.height, pageConfig.width];
    }

    // Create PDF
    const pdf = new jsPDF({
      orientation: orientation,
      unit: 'mm',
      format: options.pdf?.pageSize === 'auto' ? [imgWidth * 0.26458, imgHeight * 0.26458] : [pageConfig.width, pageConfig.height]
    });

    if (options.pdf?.pageSize === 'auto') {
      // Auto-fit: scale image to fit exactly
      pdf.addImage(imgData, 'PNG', 0, 0, imgWidth * 0.26458, imgHeight * 0.26458);
    } else {
      // Fixed page size: scale to fit within margins
      const margin = 10;
      const maxWidth = pageConfig.width - (margin * 2);
      const maxHeight = pageConfig.height - (margin * 2);
      
      const scaleFactor = Math.min(maxWidth / (imgWidth * 0.26458), maxHeight / (imgHeight * 0.26458));
      const scaledWidth = (imgWidth * 0.26458) * scaleFactor;
      const scaledHeight = (imgHeight * 0.26458) * scaleFactor;
      
      const x = (pageConfig.width - scaledWidth) / 2;
      const y = (pageConfig.height - scaledHeight) / 2;
      
      pdf.addImage(imgData, 'PNG', x, y, scaledWidth, scaledHeight);
    }

    // Save the PDF
    pdf.save(options.fileName || 'process-flow.pdf');
  } catch (error) {
    console.error('Error exporting to PDF:', error);
    throw error;
  }
}

// Define methods to expose to parent components
defineExpose({
  updateNode,
  addNode,
  removeNode,
  fitView,
  syncCanvas,
  centerOnNode,
  // Add Vue Flow save/restore methods
  toObject,
  fromObject,
  // Add export methods
  exportToPNG,
  exportToPDF,
  // Add direct access to Vue Flow methods for production fallbacks
  setNodes: (newNodes) => {
    try {
      if (Array.isArray(newNodes)) {
        nodes.value = newNodes;
      }
    } catch (error) {
      console.error("Error in setNodes:", error);
    }
  },
  setEdges: (newEdges) => {
    try {
      if (Array.isArray(newEdges)) {
        edges.value = newEdges;
      }
    } catch (error) {
      console.error("Error in setEdges:", error);
    }
  },
  // Provide access to the flow instance
  get flowInstance() {
    return flowInstance;
  },
  // Provide direct access to nodes and edges
  get nodes() {
    return nodes.value;
  },
  get edges() {
    return edges.value;
  },
  // Add method to get selected elements
  getSelectedElements: () => {
    try {
      // First try to get selection from Vue Flow instance
      if (flowInstance) {
        const selection = flowInstance.getSelection();
        if (selection && (selection.nodes?.length > 0 || selection.edges?.length > 0)) {
          return {
            nodes: selection.nodes || [],
            edges: selection.edges || []
          };
        }
      }
      
      // Fallback: check for selected property on nodes and edges
      const selectedNodes = nodes.value.filter(node => node.selected === true);
      const selectedEdges = edges.value.filter(edge => edge.selected === true);
      
      // If still no selection, try DOM-based detection
      if (selectedNodes.length === 0 && selectedEdges.length === 0) {
        // Look for selected nodes in the DOM
        const selectedNodeElements = document.querySelectorAll('.vue-flow__node.selected');
        const selectedEdgeElements = document.querySelectorAll('.vue-flow__edge.selected');
        
        // Convert DOM elements to node/edge data
        const domSelectedNodes = [];
        const domSelectedEdges = [];
        
        selectedNodeElements.forEach(element => {
          const nodeId = element.getAttribute('data-id');
          if (nodeId) {
            const node = nodes.value.find(n => n.id === nodeId);
            if (node) {
              domSelectedNodes.push(node);
            }
          }
        });
        
        selectedEdgeElements.forEach(element => {
          const edgeId = element.getAttribute('data-id');
          if (edgeId) {
            const edge = edges.value.find(e => e.id === edgeId);
            if (edge) {
              domSelectedEdges.push(edge);
            }
          }
        });
        
        if (domSelectedNodes.length > 0 || domSelectedEdges.length > 0) {
          return { nodes: domSelectedNodes, edges: domSelectedEdges };
        }
      }
      
      return { nodes: selectedNodes, edges: selectedEdges };
    } catch (error) {
      console.warn('Error getting selected elements:', error);
      return { nodes: [], edges: [] };
    }
  }
});

// Update an existing node
function updateNode(nodeId, newData) {
  const nodeToUpdate = nodes.value.find((node) => node.id === nodeId);
  if (!nodeToUpdate) {
    console.warn(`Canvas updateNode: Node ${nodeId} not found`);
    return;
  }

  // Update the node properties - handle all provided data
  if (newData.label !== undefined) {
    nodeToUpdate.label = newData.label;
  }

  // Update the node data - merge with existing data
  if (newData.data !== undefined) {
    nodeToUpdate.data = {
      ...nodeToUpdate.data,
      ...newData.data,
    };
  }

  // Update position if provided
  if (newData.position !== undefined) {
    nodeToUpdate.position = { ...newData.position };
  }

  // Update style if provided
  if (newData.style !== undefined) {
    nodeToUpdate.style = {
      ...(nodeToUpdate.style || {}),
      ...newData.style,
    };

    // Also apply style directly to DOM element for immediate visual feedback
    nextTick(() => {
      const nodeElement = document.querySelector(`[data-id="${nodeId}"]`);
      if (nodeElement) {
        // Apply styles directly to the DOM element
        Object.entries(newData.style).forEach(([key, value]) => {
          nodeElement.style[key] = value;
        });
      }
    });
  }

  // Force re-render by updating node internals
  updateNodeInternals([nodeId]);

  return nodeToUpdate;
}

// Add a new node to the canvas
function addNode(node) {
  addNodes([node]);
  return node;
}

// Remove a node from the canvas
function removeNode(nodeId) {
  const nodeToRemove = nodes.value.find((node) => node.id === nodeId);
  if (!nodeToRemove) return;

  removeNodes([nodeToRemove]);
  return nodeToRemove;
}

// Manual sync function for explicit canvas updates
function syncCanvas(newNodes, newEdges) {
  console.log(
    "🔄 Manual canvas sync requested - nodes:",
    newNodes?.length || 0,
    "edges:",
    newEdges?.length || 0
  );

  // Use a small delay to ensure any pending Vue Flow operations complete
  setTimeout(async () => {
    try {
      // Sync nodes first
      if (newNodes && Array.isArray(newNodes)) {
        const currentNodeIds = new Set(nodes.value.map((n) => n.id));
        const newNodeIds = new Set(newNodes.map((n) => n.id));

        console.log(
          "📊 Current canvas nodes:",
          currentNodeIds.size,
          "New nodes:",
          newNodeIds.size
        );

        // Remove nodes that are no longer in the new list
        // Be more conservative - only remove if we're sure it's not a temporary state
        const nodesToRemove = nodes.value.filter(
          (node) => !newNodeIds.has(node.id)
        );

        // Only remove nodes if the new list is not empty (prevents accidental clearing)
        if (nodesToRemove.length > 0 && newNodes.length > 0) {
          console.log(
            "🗑️ Removing nodes:",
            nodesToRemove.map((n) => n.id)
          );
          removeNodes(nodesToRemove);
        } else if (nodesToRemove.length > 0 && newNodes.length === 0) {
          console.log(
            "⚠️ Not removing nodes - new list is empty, might be temporary state"
          );
        }

        // Add new nodes that aren't already present
        const nodesToAdd = newNodes.filter(
          (node) => !currentNodeIds.has(node.id)
        );
        if (nodesToAdd.length > 0) {
          console.log(
            "➕ Adding new nodes:",
            nodesToAdd.map((n) => n.id)
          );
          addNodes([...nodesToAdd]);
        }

        // Update existing nodes - STRICTLY PRESERVE CANVAS POSITIONS
        newNodes.forEach((newNode) => {
          const existingNode = nodes.value.find((n) => n.id === newNode.id);
          if (existingNode) {
            // FIXED: NEVER update positions for existing nodes during manual sync
            // The canvas is the source of truth for positions once a node exists
            // Only update data and label, preserve the current canvas position
            Object.assign(existingNode, {
              label: newNode.label,
              data: { ...newNode.data },
              // ALWAYS keep the current canvas position - never overwrite from store
              position: existingNode.position,
            });
            // Always force re-render for property changes
            updateNodeInternals([newNode.id]);
          }
        });

        // Wait for nodes to be fully processed before handling edges
        await nextTick();
        await new Promise((resolve) => setTimeout(resolve, 100));
      }

      console.log(
        "📊 Canvas state after node sync - nodes:",
        nodes.value.length,
        "edges:",
        edges.value.length
      );

      // Sync edges after nodes are updated - CRITICAL: Only if we have nodes
      if (newEdges && Array.isArray(newEdges) && nodes.value.length > 0) {
        const currentEdgeIds = new Set(edges.value.map((e) => e.id));
        const newEdgeIds = new Set(newEdges.map((e) => e.id));

        console.log(
          "📊 Current canvas edges:",
          currentEdgeIds.size,
          "New edges:",
          newEdgeIds.size
        );

        // CRITICAL: Only remove edges that are definitely not in the new list
        // Be more conservative about edge removal to prevent accidental deletions
        const edgesToRemove = edges.value.filter((edge) => {
          const shouldRemove = !newEdgeIds.has(edge.id);
          if (shouldRemove) {
            // Double-check that both source and target nodes still exist
            const sourceExists = nodes.value.some(
              (node) => node.id === edge.source
            );
            const targetExists = nodes.value.some(
              (node) => node.id === edge.target
            );

            // Only remove if the edge is truly not needed OR if nodes don't exist
            return !sourceExists || !targetExists;
          }
          return false;
        });

        if (edgesToRemove.length > 0) {
          console.log(
            "🗑️ Removing edges:",
            edgesToRemove.map((e) => `${e.source}->${e.target} (${e.id})`)
          );
          removeEdges(edgesToRemove);
        }

        // Add new edges that aren't already present
        const edgesToAdd = newEdges.filter(
          (edge) => !currentEdgeIds.has(edge.id)
        );
        if (edgesToAdd.length > 0) {
          console.log(
            "➕ Adding new edges:",
            edgesToAdd.map((e) => `${e.source}->${e.target} (${e.id})`)
          );

          // Verify nodes exist and add handles
          const validEdges = edgesToAdd.filter((edge) => {
            const sourceExists = nodes.value.some(
              (node) => node.id === edge.source
            );
            const targetExists = nodes.value.some(
              (node) => node.id === edge.target
            );

            if (!sourceExists || !targetExists) {
              console.warn(
                `⚠️ Skipping edge ${edge.id}: source ${edge.source} exists: ${sourceExists}, target ${edge.target} exists: ${targetExists}`
              );
              return false;
            }

            return true;
          });

          console.log("✅ Valid edges to add:", validEdges.length);

          const edgesWithHandles = validEdges.map((edge) => {
            // If edge already has sourceHandle and targetHandle, use them
            if (edge.sourceHandle && edge.targetHandle) {
              console.log(
                `🔗 Edge ${edge.id} already has handles: ${edge.sourceHandle} -> ${edge.targetHandle}`
              );
              return edge;
            }

            const sourceNode = nodes.value.find((n) => n.id === edge.source);
            const targetNode = nodes.value.find((n) => n.id === edge.target);

            let sourceHandle = edge.sourceHandle;
            let targetHandle = edge.targetHandle;

            if (!sourceHandle && sourceNode) {
              if (sourceNode.type === "start") {
                sourceHandle = `${edge.source}-bottom`;
              } else if (sourceNode.type === "gateway") {
                sourceHandle = `${edge.source}-right`;
              } else {
                sourceHandle = `${edge.source}-bottom`;
              }
            }

            if (!targetHandle && targetNode) {
              if (targetNode.type === "end") {
                targetHandle = `${edge.target}-top`;
              } else {
                targetHandle = `${edge.target}-top`;
              }
            }

            console.log(
              `🔗 Generated handles for edge ${edge.id}: ${sourceHandle} -> ${targetHandle}`
            );
            return { ...edge, sourceHandle, targetHandle };
          });

          if (edgesWithHandles.length > 0) {
            console.log(
              "✅ Adding edges with handles:",
              edgesWithHandles.length
            );
            addEdges([...edgesWithHandles]);
          }
        }

        // Update existing edges - preserve handles and only update changed properties
        let updatedEdgeCount = 0;
        newEdges.forEach((newEdge) => {
          const existingEdge = edges.value.find((e) => e.id === newEdge.id);
          if (existingEdge) {
            // Check if update is needed
            const needsUpdate =
              existingEdge.label !== newEdge.label ||
              JSON.stringify(existingEdge.style) !==
                JSON.stringify(newEdge.style);

            if (needsUpdate) {
              Object.assign(existingEdge, {
                label: newEdge.label,
                // CRITICAL: Preserve existing handles if they exist
                sourceHandle: existingEdge.sourceHandle || newEdge.sourceHandle,
                targetHandle: existingEdge.targetHandle || newEdge.targetHandle,
                style: newEdge.style ? { ...newEdge.style } : undefined,
              });
              updatedEdgeCount++;
            }
          }
        });

        if (updatedEdgeCount > 0) {
          console.log("🔄 Updated existing edges:", updatedEdgeCount);
        }
      } else if (newEdges && Array.isArray(newEdges) && newEdges.length > 0) {
        console.warn(
          "⚠️ Cannot add edges: nodes not ready. Node count:",
          nodes.value.length
        );
      }

      console.log(
        "✅ Canvas sync completed - final state: nodes:",
        nodes.value.length,
        "edges:",
        edges.value.length
      );
    } catch (error) {
      console.error("❌ Error during canvas sync:", error);
    }
  }, 50); // Small delay to allow any pending operations to complete
}

// Add Vue Flow save method - captures complete flow state including viewport
function toObject() {
  try {
    // Create flow object using only essential data, excluding Vue Flow internals
    const flowObject = {
      nodes: nodes.value.map((node) => ({
        id: node.id,
        type: node.type,
        label: node.label,
        position: { ...node.position },
        data: { ...node.data },
        // Exclude: events, dragging, dimensions, handleBounds, computedPosition, etc.
      })),
      edges: edges.value.map((edge) => ({
        id: edge.id,
        source: edge.source,
        target: edge.target,
        sourceHandle: edge.sourceHandle,
        targetHandle: edge.targetHandle,
        label: edge.label || "",
        type: edge.type || "smoothstep",
        animated: edge.animated !== undefined ? edge.animated : true,
        data: edge.data ? { ...edge.data } : {},
        // Exclude: events, sourceNode, targetNode, sourceX, sourceY, targetX, targetY
      })),
      viewport: getViewport(),
    };

    return flowObject;
  } catch (error) {
    console.error("Error capturing flow state:", error);

    // Return a safe fallback object
    return {
      nodes: [],
      edges: [],
      viewport: { x: 0, y: 0, zoom: 1 },
    };
  }
}

// Add Vue Flow restore method - properly restores complete flow state
function fromObject(flowObject) {
  if (!flowObject) {
    console.warn("No flow object provided for restoration");
    return Promise.resolve();
  }

  if (!flowObject.nodes && !flowObject.edges) {
    console.warn("Flow object contains no nodes or edges");
    return Promise.resolve();
  }

  return new Promise((resolve) => {
    try {
      // Clear updating flags to ensure clean restoration
      isUpdatingNodes.value = false;
      isUpdatingEdges.value = false;

      // Wait for any pending operations to complete
      setTimeout(async () => {
        try {
          // Check if we're restoring the same data that's already there
          const currentNodes = nodes.value.map((n) => ({
            id: n.id,
            type: n.type,
            position: n.position,
          }));
          const newNodes =
            flowObject.nodes?.map((n) => ({
              id: n.id,
              type: n.type,
              position: n.position,
            })) || [];

          const nodesAreSame =
            currentNodes.length === newNodes.length &&
            currentNodes.every(
              (node, index) =>
                node.id === newNodes[index]?.id &&
                node.type === newNodes[index]?.type
            );

          // Only clear if we're actually restoring different data
          if (!nodesAreSame) {
            // 1. First, clear existing state
            if (nodes.value.length > 0) {
              console.log("🔄 Clearing existing nodes for restoration");
              removeNodes([...nodes.value]);
            }
            if (edges.value.length > 0) {
              console.log("🔄 Clearing existing edges for restoration");
              removeEdges([...edges.value]);
            }
          } else {
            console.log("✅ Nodes are the same, skipping clear operation");
          }

          // Wait for clearing to complete
          await nextTick();

          // 2. Restore nodes first (only if we cleared or if no nodes exist)
          if (
            flowObject.nodes &&
            Array.isArray(flowObject.nodes) &&
            flowObject.nodes.length > 0 &&
            (!nodesAreSame || nodes.value.length === 0)
          ) {
            const nodesToRestore = flowObject.nodes.map((node) => ({
              id: node.id,
              type: node.type,
              label: node.label || "",
              position: node.position || { x: 0, y: 0 },
              data: node.data || {},
              // Only include essential properties needed for Vue Flow
            }));

            console.log("🔄 Restoring", nodesToRestore.length, "nodes");
            addNodes(nodesToRestore);
            await nextTick();

            // Wait a bit more for nodes to be fully initialized
            await new Promise((resolve) => setTimeout(resolve, 100));
          } else if (nodesAreSame) {
            console.log("✅ Nodes already exist, skipping node restoration");
          }

          // 3. Restore edges after nodes are ready (only if we cleared or if no edges exist)
          if (
            flowObject.edges &&
            Array.isArray(flowObject.edges) &&
            flowObject.edges.length > 0 &&
            nodes.value.length > 0 &&
            (!nodesAreSame || edges.value.length === 0)
          ) {
            // Verify all edges have valid source and target nodes
            const validEdges = flowObject.edges.filter((edge) => {
              const sourceExists = nodes.value.some(
                (node) => node.id === edge.source
              );
              const targetExists = nodes.value.some(
                (node) => node.id === edge.target
              );

              if (!sourceExists || !targetExists) {
                console.warn(
                  `Skipping invalid edge ${edge.id}: source ${edge.source} exists: ${sourceExists}, target ${edge.target} exists: ${targetExists}`
                );
                return false;
              }

              return true;
            });

            if (validEdges.length > 0) {
              // Clean edge data before adding
              const cleanEdges = validEdges.map((edge) => ({
                id: edge.id,
                source: edge.source,
                target: edge.target,
                sourceHandle: edge.sourceHandle,
                targetHandle: edge.targetHandle,
                label: edge.label || "",
                type: edge.type || "custom",
                animated: edge.animated !== undefined ? edge.animated : true,
                data: edge.data || {},
                // Only include essential properties
              }));

              console.log("🔄 Restoring", cleanEdges.length, "edges");
              addEdges(cleanEdges);
              await nextTick();
            }
          } else if (nodesAreSame) {
            console.log("✅ Edges already exist, skipping edge restoration");
          }

          // 4. Finally, restore viewport position and zoom
          if (flowObject.viewport && typeof flowObject.viewport === "object") {
            const viewport = {
              x: flowObject.viewport.x || 0,
              y: flowObject.viewport.y || 0,
              zoom: flowObject.viewport.zoom || 1,
            };

            setViewport(viewport, { duration: 0 }); // No animation for restore
          }

          resolve();
        } catch (error) {
          console.error("Error during flow state restoration:", error);
          resolve(); // Resolve anyway to prevent hanging
        }
      }, 100); // Small delay to ensure clean state
    } catch (error) {
      console.error("Error in fromObject:", error);
      resolve(); // Resolve anyway to prevent hanging
    }
  });
}
</script>

<template>
  <div class="process-flow-container" @drop="onDrop" @dragover="onDragOver">
    <VueFlow
      v-if="nodes.length > 0"
      v-bind="flowOptions"
      class="bg-slate-50 process-flow"
      :nodeTypes="customNodeTypes"
      :edgeTypes="edgeTypes"
      @node-click="onNodeClick"
      @edge-click="onEdgeClick"
      @pane-click="onPaneClick"
      @connect="handleConnect"
      @nodeDoubleClick="onNodeDelete"
      @edgeDoubleClick="onEdgeDelete"
      @keyup.delete="onDeleteKeyPress"
      @selectionChange="onSelectionChange"
      @nodesSelection="onNodesSelection"
    >
      <!-- Global SVG definitions for arrows -->
      <svg style="position: absolute; top: 0; left: 0; width: 0; height: 0">
        <defs>
          <marker
            id="arrow"
            viewBox="0 0 10 10"
            refX="9"
            refY="3"
            markerWidth="6"
            markerHeight="6"
            orient="auto"
            markerUnits="strokeWidth"
          >
            <path d="M0,0 L0,6 L9,3 z" fill="#555" />
          </marker>
          <marker
            id="arrow-selected"
            viewBox="0 0 10 10"
            refX="9"
            refY="3"
            markerWidth="6"
            markerHeight="6"
            orient="auto"
            markerUnits="strokeWidth"
          >
            <path d="M0,0 L0,6 L9,3 z" fill="#ff6b6b" />
          </marker>
          <marker
            id="arrow-animated"
            viewBox="0 0 10 10"
            refX="9"
            refY="3"
            markerWidth="6"
            markerHeight="6"
            orient="auto"
            markerUnits="strokeWidth"
          >
            <path d="M0,0 L0,6 L9,3 z" fill="#3b82f6" />
          </marker>
        </defs>
      </svg>
      <Background pattern-color="#aaa" gap="20" />
      <Controls />
      <MiniMap />
      <!-- Custom edge template -->
      <template #edge-custom="edgeProps">
        <InteractiveArrowEdge v-bind="edgeProps" />
      </template>
      <template #edge-label="{ label }">
        <div class="edge-label">{{ label }}</div>
      </template>
      
      <!-- Collapsible Help Guide -->
      <Panel position="top-right" class="help-guide">
        <div class="help-guide-content">
          <button 
            @click="toggleHelpGuide" 
            class="help-toggle-btn"
            :class="{ 'expanded': showHelpGuide }"
          >
            <Icon name="material-symbols:help-outline" class="w-4 h-4" />
            <span v-if="showHelpGuide">Hide Help</span>
            <span v-else>Show Help</span>
          </button>
          
          <div v-if="showHelpGuide" class="help-content">
            <div class="help-section">
              <h4 class="help-title">Selection</h4>
              <ul class="help-list">
                <li>• Click: Select single node</li>
                <li>• Drag: Select multiple nodes</li>
                <li>• Ctrl+Click: Add to selection</li>
                <li>• Drag selected nodes together</li>
              </ul>
            </div>
            
            <div class="help-section">
              <h4 class="help-title">Actions</h4>
              <ul class="help-list">
                <li>• Delete: Remove selected</li>
                <li>• Double-click: Remove node/edge</li>
                <li>• Drag between nodes: Connect</li>
              </ul>
            </div>
            
            <div class="help-section">
              <h4 class="help-title">Edges</h4>
              <ul class="help-list">
                <li>• Select edge for controls</li>
                <li>• Blue dot: Reposition</li>
                <li>• Reset: Restore path</li>
              </ul>
            </div>
          </div>
        </div>
      </Panel>

      <!-- Collapsible Validation Panel -->
      <Panel position="top-left" class="validation-panel">
        <div class="validation-panel-content">
          <button 
            @click="toggleValidationPanel" 
            class="validation-toggle-btn"
            :class="{ 'expanded': showValidationPanel }"
          >
            <Icon 
              :name="processStore.overallValidationStatus === 'error' ? 'material-symbols:error' : 
                     processStore.overallValidationStatus === 'warning' ? 'material-symbols:warning' : 
                     'material-symbols:check-circle'" 
              class="w-4 h-4"
            />
            <span v-if="showValidationPanel">Hide Validation</span>
            <span v-else>
              Validation
              <span v-if="processStore.validationSummary.totalIssues > 0" class="inline-badge">
                {{ processStore.validationSummary.totalIssues }}
              </span>
            </span>
          </button>
          
          <div v-if="showValidationPanel" class="validation-content">
            <div class="validation-header">
              <div class="validation-status" :class="processStore.overallValidationStatus">
                <span class="status-text">
                  {{ processStore.overallValidationStatus === 'error' ? 'Errors Found' : 
                     processStore.overallValidationStatus === 'warning' ? 'Warnings Found' : 'All Valid' }}
                </span>
              </div>
            </div>
            
            <div v-if="processStore.validationSummary.totalIssues > 0" class="validation-summary">
              <div class="summary-stats">
                <span v-if="processStore.validationSummary.errors > 0" class="stat-item error">
                  {{ processStore.validationSummary.errors }} error{{ processStore.validationSummary.errors > 1 ? 's' : '' }}
                </span>
                <span v-if="processStore.validationSummary.warnings > 0" class="stat-item warning">
                  {{ processStore.validationSummary.warnings }} warning{{ processStore.validationSummary.warnings > 1 ? 's' : '' }}
                </span>
                <span v-if="processStore.validationSummary.infos > 0" class="stat-item info">
                  {{ processStore.validationSummary.infos }} info{{ processStore.validationSummary.infos > 1 ? 's' : '' }}
                </span>
              </div>
            </div>
            
            <div v-else-if="nodes.length > 0" class="validation-success">
              <Icon name="material-symbols:check-circle" class="w-4 h-4 text-green-500" />
              <span class="text-green-600 text-sm">Process validation passed</span>
            </div>
            
            <div v-else class="validation-empty">
              <span class="text-gray-500 text-sm">Add nodes to validate process</span>
            </div>
            
            <div v-if="isValidating" class="validation-loading">
              <Icon name="material-symbols:refresh" class="w-4 h-4 animate-spin" />
              <span class="text-sm text-gray-600">Validating...</span>
            </div>
          </div>
        </div>
      </Panel>
    </VueFlow>
  </div>
</template>

<style>
/* Node styles from nodeStyles.js are imported globally in a plugin */
.process-flow-container {
  width: 100%;
  min-height: 500px;
  border: 1px solid #e2e8f0;
  border-radius: 0;
  overflow: hidden;
  position: relative;
  flex: 1;
}

/* Make sure VueFlow takes full height */
:deep(.vue-flow) {
  height: 100% !important;
}

/* Ensure the flow wrapper fills container */
:deep(.vue-flow__container) {
  height: 100% !important;
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .process-flow-container {
    height: calc(100vh - 150px);
    min-height: 400px;
  }
}

@media (max-width: 480px) {
  .process-flow-container {
    height: calc(100vh - 120px);
    min-height: 300px;
  }

  :deep(.vue-flow__controls) {
    transform: scale(0.8);
    transform-origin: bottom right;
  }

  :deep(.vue-flow__minimap) {
    transform: scale(0.8);
    transform-origin: top right;
  }
}

.edge-label {
  background-color: white;
  padding: 3px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
  border: 1.5px solid #3b82f6;
  color: #1e40af;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.2);
  white-space: nowrap;
  z-index: 10;
}

/* Vue Flow custom styles */
:deep(.vue-flow__edge-path) {
  stroke: #555;
  stroke-width: 2px;
  transition:
    stroke-width 0.2s ease,
    stroke 0.2s ease;
}

:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
  stroke: #ff6b6b;
  stroke-width: 3px;
  filter: drop-shadow(0 0 4px rgba(255, 107, 107, 0.3));
}

:deep(.vue-flow__edge.animated .vue-flow__edge-path) {
  stroke-dasharray: 5;
  animation: flowEdgeAnimation 2s linear infinite;
}

:deep(.vue-flow__edge:hover .vue-flow__edge-path) {
  stroke-width: 3px;
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.2));
}

/* Custom edge styles */
:deep(.custom-edge) {
  cursor: pointer;
}

:deep(.custom-edge.selected) {
  filter: drop-shadow(0 0 4px rgba(255, 107, 107, 0.5));
}

/* Edge updater styles */
:deep(.vue-flow__edge-updater) {
  stroke: #3b82f6;
  fill: #3b82f6;
  stroke-width: 2;
  cursor: crosshair;
}

:deep(.vue-flow__edge-updater:hover) {
  stroke: #2563eb;
  fill: #2563eb;
}

/* Edge control point styles for better visibility */
:deep(.edge-control-point) {
  cursor: move !important;
  opacity: 0;
  transition:
    opacity 0.2s ease,
    r 0.2s ease;
}

:deep(.vue-flow__edge.selected .edge-control-point) {
  opacity: 1;
}

:deep(.edge-control-point:hover) {
  r: 6 !important;
  fill: #2563eb !important;
}

/* Add custom node icon styles */
:deep(.custom-node) {
  border-radius: 6px;
  padding: 12px;
  background: white;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  min-width: 150px;
}

:deep(.custom-node-header) {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
}

:deep(.custom-node-icon) {
  margin-right: 8px;
}

:deep(.custom-node-icon .material-icons) {
  font-size: 20px;
}

:deep(.node-start .custom-node-icon .material-icons) {
  color: #4caf50;
}

:deep(.node-end .custom-node-icon .material-icons) {
  color: #f44336;
}

:deep(.node-task .custom-node-icon .material-icons) {
  color: #2196f3;
}

:deep(.node-form .custom-node-icon .material-icons) {
  color: #9c27b0;
}

:deep(.node-gateway .custom-node-icon .material-icons) {
  color: #ff9800;
  font-size: 18px;
}

:deep(.node-script .custom-node-icon .material-icons) {
  color: #607d8b;
}

:deep(.node-html .custom-node-icon .material-icons) {
  color: #0ea5e9;
}

:deep(.custom-node-title) {
  font-weight: 500;
  flex-grow: 1;
}

:deep(.custom-node-content) {
  font-size: 12px;
  color: #666;
}

@keyframes flowEdgeAnimation {
  from {
    stroke-dashoffset: 10;
  }
  to {
    stroke-dashoffset: 0;
  }
}

.node-controls {
  display: none;
}

:deep(.vue-flow__handle) {
  width: 8px;
  height: 8px;
  background: #555;
  border: 2px solid white;
}

:deep(.vue-flow__handle:hover) {
  background: #ff6b6b;
}

:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
  stroke: #ff6b6b;
  stroke-width: 3px;
}

:deep(.vue-flow__node.selected) {
  box-shadow: 0 0 0 2px #ff6b6b;
}

/* Target both Vue Flow nodes and custom nodes - updated highlighting */
:deep(.vue-flow__node.highlighted-node),
:deep(.vue-flow__node[data-highlighted="true"]),
:deep(.vue-flow__node .custom-node.highlighted-node),
:deep(.vue-flow__node .custom-node[data-highlighted="true"]) {
  animation: highlightPulse 2s ease-in-out infinite !important;
  box-shadow:
    0 0 0 3px #3b82f6,
    0 0 20px rgba(59, 130, 246, 0.4) !important;
  z-index: 999 !important;
  transform: scale(1.05) !important;
  transition: transform 0.3s ease-in-out !important;
}

/* Additional selector for direct custom node highlighting */
:deep(.custom-node.highlighted-node),
:deep(.custom-node[data-highlighted="true"]) {
  animation: highlightPulse 2s ease-in-out infinite !important;
  box-shadow:
    0 0 0 3px #3b82f6,
    0 0 20px rgba(59, 130, 246, 0.4) !important;
  z-index: 999 !important;
  transform: scale(1.05) !important;
  transition: transform 0.3s ease-in-out !important;
}

@keyframes highlightPulse {
  0%,
  100% {
    box-shadow:
      0 0 0 3px #3b82f6,
      0 0 20px rgba(59, 130, 246, 0.4);
  }
  50% {
    box-shadow:
      0 0 0 5px #60a5fa,
      0 0 30px rgba(96, 165, 250, 0.6);
  }
}

/* Selection box styles */
:deep(.vue-flow__selection) {
  background: rgba(59, 130, 246, 0.1);
  border: 2px solid #3b82f6;
  border-radius: 4px;
  pointer-events: none;
}

:deep(.vue-flow__selection-rect) {
  fill: rgba(59, 130, 246, 0.1);
  stroke: #3b82f6;
  stroke-width: 2;
  stroke-dasharray: 5, 5;
}

/* Multi-selected nodes styling */
:deep(.vue-flow__node.selected) {
  box-shadow: 0 0 0 2px #3b82f6;
  z-index: 10;
}

:deep(.vue-flow__node.selected .custom-node) {
  box-shadow: 0 0 0 2px #3b82f6, 0 2px 5px rgba(0, 0, 0, 0.1);
}

/* Selection box animation */
@keyframes selectionPulse {
  0%, 100% {
    opacity: 0.3;
  }
  50% {
    opacity: 0.6;
  }
}

:deep(.vue-flow__selection) {
  animation: selectionPulse 2s ease-in-out infinite;
}

/* Help Guide Styles */
.help-guide {
  pointer-events: all;
}

.help-guide-content {
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  overflow: hidden;
  max-width: 280px;
  transition: all 0.3s ease;
}

.help-toggle-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  white-space: nowrap;
}

.help-toggle-btn:hover {
  background: #2563eb;
  transform: translateY(-1px);
}

.help-toggle-btn.expanded {
  background: #dc2626;
}

.help-toggle-btn.expanded:hover {
  background: #b91c1c;
}

.help-content {
  padding: 12px;
  background: white;
  border-top: 1px solid #e5e7eb;
}

.help-section {
  margin-bottom: 12px;
}

.help-section:last-child {
  margin-bottom: 0;
}

.help-title {
  font-size: 11px;
  font-weight: 600;
  color: #374151;
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.help-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.help-list li {
  font-size: 11px;
  color: #6b7280;
  margin-bottom: 3px;
  line-height: 1.3;
}

.help-list li:last-child {
  margin-bottom: 0;
}

/* Validation Panel Styles */
.validation-panel {
  pointer-events: all;
}

.validation-panel-content {
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  overflow: hidden;
  max-width: 280px;
  transition: all 0.3s ease;
}

.validation-toggle-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #059669;
  color: white;
  border: none;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  white-space: nowrap;
  width: 100%;
}

.validation-toggle-btn:hover {
  background: #047857;
  transform: translateY(-1px);
}

.validation-toggle-btn.expanded {
  background: #dc2626;
}

.validation-toggle-btn.expanded:hover {
  background: #b91c1c;
}

.inline-badge {
  background: rgba(255, 255, 255, 0.3);
  padding: 2px 6px;
  border-radius: 10px;
  font-size: 10px;
  margin-left: 4px;
}

.validation-content {
  padding: 12px;
  background: white;
  border-top: 1px solid #e5e7eb;
}

.validation-header {
  margin-bottom: 8px;
}

.validation-status {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 4px;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
}

.validation-status.error {
  color: #dc2626;
  background: #fef2f2;
}

.validation-status.warning {
  color: #d97706;
  background: #fffbeb;
}

.validation-status.valid {
  color: #059669;
  background: #f0fdf4;
}

.validation-summary {
  margin-top: 8px;
}

.summary-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.stat-item {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 3px;
  font-weight: 500;
}

.stat-item.error {
  color: #dc2626;
  background: #fef2f2;
}

.stat-item.warning {
  color: #d97706;
  background: #fffbeb;
}

.stat-item.info {
  color: #2563eb;
  background: #eff6ff;
}

.validation-success {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
}

.validation-empty {
  margin-top: 8px;
}

.validation-loading {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
}

.status-text {
  font-size: 10px;
}
</style>
