using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using Gyvr.Mythril2D;

namespace M2DVisualQuestEditor
{
    public class QuestGraphView : GraphView
    {
        private QuestGraphEditorWindow m_editorWindow;
        private QuestGraphData m_graphData;
        private Gyvr.Mythril2D.Quest m_quest;

        private Dictionary<string, Node> m_nodeViews = new Dictionary<string, Node>();
        private Manipulator m_contentDragger;
        private SelectionDragger m_selectionDragger;
        private RectangleSelector m_rectangleSelector;
        private bool m_isPanMode = false;
        
        private static Font s_editorFont;
        private static Font EditorFont
        {
            get
            {
                if (s_editorFont == null)
                {
                    string[] fontNames = { "Helvetica", "Helvetica Neue", "Arial", "Liberation Sans", "DejaVu Sans" };
                    foreach (var fontName in fontNames)
                    {
                        s_editorFont = Font.CreateDynamicFontFromOSFont(fontName, 12);
                        if (s_editorFont != null) break;
                    }
                }
                return s_editorFont;
            }
        }

        public QuestGraphView(QuestGraphEditorWindow editorWindow)
        {
            m_editorWindow = editorWindow;

            var stylesheet = Resources.Load<StyleSheet>("QuestGraphViewStyles");
            if (stylesheet != null)
            {
                styleSheets.Add(stylesheet);
            }
            
            var gridStylesheet = Resources.Load<StyleSheet>("QuestGraphGridStyles");
            if (gridStylesheet != null)
            {
                styleSheets.Add(gridStylesheet);
            }
            
            var grid = new GridBackground();
            grid.StretchToParentSize();
            Insert(0, grid);
            
            EditorApplication.update += OnEditorUpdate;
            
            Undo.undoRedoPerformed += OnUndoRedoPerformed;
            
            graphViewChanged += OnGraphViewChanged;
            
            RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
            RegisterCallback<MouseDownEvent>(evt => Focus());
            
            SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);

            m_contentDragger = new ContentDragger();
            this.AddManipulator(m_contentDragger);
            
            m_selectionDragger = new SelectionDragger();
            this.AddManipulator(m_selectionDragger);
            
            m_rectangleSelector = new RectangleSelector();
            this.AddManipulator(m_rectangleSelector);

            focusable = true;
            
            style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f);
            contentViewContainer.style.backgroundColor = Color.clear;

            var toolbar = new VisualElement { style = { flexDirection = FlexDirection.Row, paddingTop = 5, paddingLeft = 5, paddingRight = 5, height = 35 } };
            Add(toolbar);
            
            deleteSelection = (operationName, askUser) =>
            {
                
                var edgesToDelete = new List<Edge>();
                var nodesToDelete = new List<Node>();
                var stickyNotesToDelete = new List<StickyNote>();
                
                foreach (var element in selection)
                {
                    if (element is Edge edge)
                    {
                        if (!edgesToDelete.Contains(edge))
                            edgesToDelete.Add(edge);
                    }
                    else if (element is StickyNote stickyNote)
                    {
                        stickyNotesToDelete.Add(stickyNote);
                    }
                    else if (element is Node node)
                    {
                        nodesToDelete.Add(node);
                        var connectedEdges = edges.ToList().Where(e => 
                            e.output?.node == node || e.input?.node == node);
                        foreach (var connectedEdge in connectedEdges)
                        {
                            if (!edgesToDelete.Contains(connectedEdge))
                                edgesToDelete.Add(connectedEdge);
                        }
                    }
                }

                foreach (var stickyNote in stickyNotesToDelete)
                {
                    RemoveElement(stickyNote);
                }
                
                foreach (var edge in edgesToDelete)
                {
                    edge.input?.Disconnect(edge);
                    edge.output?.Disconnect(edge);
                    RemoveElement(edge);
                }
                
                foreach (var node in nodesToDelete)
                {
                    
                    if (node.name == "quest-info")
                    {
                        continue;
                    }
                    
                    if (node.userData is Gyvr.Mythril2D.DialogueSequence dialogue)
                    {
                        string dialogueName = dialogue.name;
                        bool isEssential = (dialogueName.Contains("_Offer") || dialogueName.Contains("_Completed") || 
                                          (dialogueName.Contains("_Hint") && !dialogueName.Contains("HintOverride")));
                        if (isEssential)
                        {
                            continue;
                        }
                        
                        Undo.RecordObject(m_quest, "Delete Dialogue");
                        string assetPath = AssetDatabase.GetAssetPath(dialogue);
                        if (AssetDatabase.IsSubAsset(dialogue))
                        {
                            Undo.DestroyObjectImmediate(dialogue);
                        }
                        else if (!string.IsNullOrEmpty(assetPath))
                        {
                            AssetDatabase.DeleteAsset(assetPath);
                        }
                    }
                    else if (node.userData is System.Tuple<Gyvr.Mythril2D.QuestTask, int> taskData)
                    {
                        var task = taskData.Item1;
                        int taskIndex = taskData.Item2;
                        
                        Undo.RecordObject(m_quest, "Delete Task");
                        var tasksList = new List<Gyvr.Mythril2D.QuestTask>(m_quest.tasks);
                        if (taskIndex >= 0 && taskIndex < tasksList.Count)
                        {
                            var serializedQuest = new SerializedObject(m_quest);
                            var tasksProperty = serializedQuest.FindProperty("m_tasks");
                            if (tasksProperty != null)
                            {
                                tasksProperty.DeleteArrayElementAtIndex(taskIndex);
                                serializedQuest.ApplyModifiedProperties();
                            }
                        }
                        
                        string assetPath = AssetDatabase.GetAssetPath(task);
                        if (AssetDatabase.IsSubAsset(task))
                        {
                            Undo.DestroyObjectImmediate(task);
                        }
                        else if (!string.IsNullOrEmpty(assetPath))
                        {
                            AssetDatabase.DeleteAsset(assetPath);
                        }
                    }
                    
                    if (m_nodeViews.ContainsKey(node.name))
                    {
                        m_nodeViews.Remove(node.name);
                    }
                    
                    if (m_graphData != null)
                    {
                        Undo.RecordObject(m_graphData, "Delete Node");
                        m_graphData.RemoveNode(node.name);
                        EditorUtility.SetDirty(m_graphData);
                    }
                    
                    RemoveElement(node);
                }
                
                AssetDatabase.SaveAssets();
                selection.Clear();
                RefreshQuestWarnings();
            };
        }
        
        private void CreateAndAddEdge(Port outputPort, Port inputPort)
        {
            if (outputPort == null || inputPort == null)
                return;

            var edge = outputPort.ConnectTo(inputPort);
            AddElement(edge);

            if (m_graphData != null)
            {
                var outputNode = outputPort.node as Node;
                var inputNode = inputPort.node as Node;
                
                if (outputNode != null && inputNode != null)
                {
                    var connectionData = new ConnectionData
                    {
                        fromNodeId = outputNode.name,
                        toNodeId = inputNode.name,
                        fromPortName = outputPort.portName ?? "",
                        toPortName = inputPort.portName ?? ""
                    };
                    
                    m_graphData.AddConnection(connectionData);
                    EditorUtility.SetDirty(m_graphData);
                }
            }
        }
        
        private GraphViewChange OnGraphViewChanged(GraphViewChange change)
        {
            if (m_graphData == null)
                return change;

            if (change.movedElements != null && change.movedElements.Count > 0)
            {
                Undo.RecordObject(m_graphData, "Move Nodes");
                
                foreach (var element in change.movedElements)
                {
                    if (element is Node node)
                    {
                        var nodeData = m_graphData.GetNode(node.name);
                        if (nodeData != null)
                        {
                            Vector2 newPos = node.GetPosition().position;
                            nodeData.position = newPos;
                        }
                    }
                }
                
                EditorUtility.SetDirty(m_graphData);
                AssetDatabase.SaveAssets();
            }

            if (change.edgesToCreate != null)
            {
                Undo.RecordObject(m_graphData, "Create Connection");
                foreach (var edge in change.edgesToCreate)
                {
                    var outputNode = edge.output.node as Node;
                    var inputNode = edge.input.node as Node;
                    
                    if (outputNode != null && inputNode != null)
                    {
                        var connectionData = new ConnectionData
                        {
                            fromNodeId = outputNode.name,
                            toNodeId = inputNode.name,
                            fromPortName = edge.output.portName ?? "",
                            toPortName = edge.input.portName ?? ""
                        };
                        
                        m_graphData.AddConnection(connectionData);
                        EditorUtility.SetDirty(m_graphData);
                        AssetDatabase.SaveAssets();

                        if (outputNode.name.StartsWith("task-") && inputNode.userData is DialogueSequence dialogueData)
                        {
                            if (int.TryParse(outputNode.name.Replace("task-", ""), out int taskIndex))
                            {
                                if (taskIndex < m_quest.tasks.Length && m_quest.tasks[taskIndex] is TalkToNPCTask talkToTask)
                                {
                                    var serializedTask = new SerializedObject(talkToTask);
                                    serializedTask.FindProperty("dialogue").objectReferenceValue = dialogueData;
                                    serializedTask.ApplyModifiedProperties();
                                    AssetDatabase.SaveAssets();
                                }
                            }
                        }
                        
                        if (outputNode.userData is DialogueSequence sourceDialogue &&
                            inputNode.userData is DialogueSequence targetDialogue)
                        {
                            
                            if (!string.IsNullOrEmpty(edge.output.portName) && edge.output.portName.StartsWith("option-"))
                            {
                                if (int.TryParse(edge.output.portName.Replace("option-", ""), out int optionIndex))
                                {
                                    if (sourceDialogue.options != null && optionIndex < sourceDialogue.options.Length)
                                    {
                                        var serializedDialogue = new SerializedObject(sourceDialogue);
                                        var optionsProperty = serializedDialogue.FindProperty("options");
                                        var optionProperty = optionsProperty.GetArrayElementAtIndex(optionIndex);
                                        var sequenceProperty = optionProperty.FindPropertyRelative("sequence");
                                        sequenceProperty.objectReferenceValue = targetDialogue;
                                        serializedDialogue.ApplyModifiedProperties();
                                        AssetDatabase.SaveAssets();
                                        
                                        string optionText = sourceDialogue.options[optionIndex].name;
                                        if (!string.IsNullOrEmpty(optionText))
                                        {
                                            inputNode.title = $"\"{optionText}\"";
                                            SaveNodeCustomTitle(inputNode.name, inputNode.title);
                                        }
                                    }
                                }
                            }
                            else if (sourceDialogue.options != null && sourceDialogue.options.Length > 0)
                            {
                                var serializedDialogue = new SerializedObject(sourceDialogue);
                                var optionsProperty = serializedDialogue.FindProperty("options");
                                var optionProperty = optionsProperty.GetArrayElementAtIndex(0);
                                var sequenceProperty = optionProperty.FindPropertyRelative("sequence");
                                sequenceProperty.objectReferenceValue = targetDialogue;
                                serializedDialogue.ApplyModifiedProperties();
                                AssetDatabase.SaveAssets();
                                
                                string optionText = sourceDialogue.options[0].name;
                                if (!string.IsNullOrEmpty(optionText))
                                {
                                    inputNode.title = $"\"{optionText}\"";
                                    SaveNodeCustomTitle(inputNode.name, inputNode.title);
                                }
                            }
                        }
                        
                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                        
                        if (outputNode.name.StartsWith("task-") && edge.output != null && edge.output.portName == "Hint Override")
                        {
                            if (int.TryParse(outputNode.name.Replace("task-", ""), out int taskIndex))
                            {
                                if (taskIndex < m_quest.tasks.Length)
                                {
                                    var task = m_quest.tasks[taskIndex];
                                    if (inputNode.userData is DialogueSequence hintOverrideDialogue)
                                    {
                                        var serializedQuest = new SerializedObject(m_quest);
                                        var overridesProperty = serializedQuest.FindProperty("m_questHintDialogueOverrides");
                                        
                                        if (overridesProperty != null)
                                        {
                                            var keysProperty = overridesProperty.FindPropertyRelative("m_keys");
                                            var valuesProperty = overridesProperty.FindPropertyRelative("m_values");
                                            
                                            if (keysProperty != null && valuesProperty != null)
                                            {
                                                int existingIndex = -1;
                                                for (int i = 0; i < keysProperty.arraySize; i++)
                                                {
                                                    if (keysProperty.GetArrayElementAtIndex(i).objectReferenceValue == task)
                                                    {
                                                        existingIndex = i;
                                                        break;
                                                    }
                                                }
                                                
                                                if (existingIndex >= 0)
                                                {
                                                    valuesProperty.GetArrayElementAtIndex(existingIndex).objectReferenceValue = hintOverrideDialogue;
                                                }
                                                else
                                                {
                                                    keysProperty.InsertArrayElementAtIndex(keysProperty.arraySize);
                                                    valuesProperty.InsertArrayElementAtIndex(valuesProperty.arraySize);
                                                    keysProperty.GetArrayElementAtIndex(keysProperty.arraySize - 1).objectReferenceValue = task;
                                                    valuesProperty.GetArrayElementAtIndex(valuesProperty.arraySize - 1).objectReferenceValue = hintOverrideDialogue;
                                                }
                                                
                                                serializedQuest.ApplyModifiedProperties();
                                                EditorUtility.SetDirty(m_quest);
                                                AssetDatabase.SaveAssets();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                    if (outputNode != null && outputNode.userData is ConditionalNPCInteractionData conditionalData)
                    {
                        // When wiring a dialogue to a conditional interaction, get the dialogue directly from the edge
                        if (inputNode != null && inputNode.userData is Gyvr.Mythril2D.DialogueSequence wiredDialogue)
                        {
                            if (conditionalData.interactionType == "DialogueInteraction")
                            {
                                conditionalData.dialogue = wiredDialogue;
                            }
                        }
                        
                        UpdateWireHintLabel(outputNode);
                        OnConditionalNPCInteractionChanged(outputNode);
                    }
                }
                
                RefreshQuestWarnings();
            }

            if (change.elementsToRemove != null)
            {
                Undo.RecordObject(m_graphData, "Delete Connection");
                
                foreach (var element in change.elementsToRemove)
                {
                    if (element is Edge edge)
                    {
                        var outputNode = edge.output?.node as Node;
                        var inputNode = edge.input?.node as Node;
                        
                        if (outputNode != null && inputNode != null)
                        {
                            if (outputNode.name.StartsWith("task-") && inputNode.userData is DialogueSequence)
                            {
                                if (int.TryParse(outputNode.name.Replace("task-", ""), out int taskIndex))
                                {
                                    if (taskIndex < m_quest.tasks.Length && m_quest.tasks[taskIndex] is TalkToNPCTask talkToTask)
                                    {
                                        var serializedTask = new SerializedObject(talkToTask);
                                        serializedTask.FindProperty("dialogue").objectReferenceValue = null;
                                        serializedTask.ApplyModifiedProperties();
                                        AssetDatabase.SaveAssets();
                                    }
                                }
                            }
                            
                            if (outputNode.userData is DialogueSequence sourceDialogue &&
                                inputNode.userData is DialogueSequence)
                            {
                                
                                if (!string.IsNullOrEmpty(edge.output.portName) && edge.output.portName.StartsWith("option-"))
                                {
                                    if (int.TryParse(edge.output.portName.Replace("option-", ""), out int optionIndex))
                                    {
                                        if (sourceDialogue.options != null && optionIndex < sourceDialogue.options.Length)
                                        {
                                            var serializedDialogue = new SerializedObject(sourceDialogue);
                                            var optionsProperty = serializedDialogue.FindProperty("options");
                                            var optionProperty = optionsProperty.GetArrayElementAtIndex(optionIndex);
                                            var sequenceProperty = optionProperty.FindPropertyRelative("sequence");
                                            sequenceProperty.objectReferenceValue = null;
                                            serializedDialogue.ApplyModifiedProperties();
                                            AssetDatabase.SaveAssets();
                                            
                                            if (inputNode != null && !inputNode.name.Contains("QuestOffer") && 
                                                !inputNode.name.Contains("QuestHint") && !inputNode.name.Contains("QuestCompleted"))
                                            {
                                                inputNode.title = "Dialogue";
                                                SaveNodeCustomTitle(inputNode.name, "");
                                            }
                                        }
                                    }
                                }
                                else if (sourceDialogue.options != null && sourceDialogue.options.Length > 0)
                                {
                                    var serializedDialogue = new SerializedObject(sourceDialogue);
                                    var optionsProperty = serializedDialogue.FindProperty("options");
                                    var optionProperty = optionsProperty.GetArrayElementAtIndex(0);
                                    var sequenceProperty = optionProperty.FindPropertyRelative("sequence");
                                    sequenceProperty.objectReferenceValue = null;
                                    serializedDialogue.ApplyModifiedProperties();
                                    AssetDatabase.SaveAssets();
                                    
                                    if (inputNode != null && !inputNode.name.Contains("QuestOffer") && 
                                        !inputNode.name.Contains("QuestHint") && !inputNode.name.Contains("QuestCompleted"))
                                    {
                                        inputNode.title = "Dialogue";
                                        SaveNodeCustomTitle(inputNode.name, "");
                                    }
                                }
                            }
                            
                            if (outputNode.name.StartsWith("task-") && edge.output?.portName == "Hint Override")
                            {
                                if (int.TryParse(outputNode.name.Replace("task-", ""), out int taskIndex))
                                {
                                    if (taskIndex < m_quest.tasks.Length)
                                    {
                                        var task = m_quest.tasks[taskIndex];
                                        var serializedQuest = new SerializedObject(m_quest);
                                        var overridesProperty = serializedQuest.FindProperty("m_questHintDialogueOverrides");
                                        
                                        if (overridesProperty != null)
                                        {
                                            var keysProperty = overridesProperty.FindPropertyRelative("m_keys");
                                            var valuesProperty = overridesProperty.FindPropertyRelative("m_values");
                                            
                                            if (keysProperty != null && valuesProperty != null)
                                            {
                                                for (int i = 0; i < keysProperty.arraySize; i++)
                                                {
                                                    if (keysProperty.GetArrayElementAtIndex(i).objectReferenceValue == task)
                                                    {
                                                        keysProperty.DeleteArrayElementAtIndex(i);
                                                        valuesProperty.DeleteArrayElementAtIndex(i);
                                                        break;
                                                    }
                                                }
                                                
                                                serializedQuest.ApplyModifiedProperties();
                                                EditorUtility.SetDirty(m_quest);
                                                AssetDatabase.SaveAssets();
                                            }
                                        }
                                    }
                                }
                            }
                            
                            m_graphData.RemoveConnection(outputNode.name, inputNode.name);
                            EditorUtility.SetDirty(m_graphData);
                            AssetDatabase.SaveAssets();
                            
                            if (outputNode.userData is ConditionalNPCInteractionData)
                            {
                                UpdateWireHintLabel(outputNode);
                                OnConditionalNPCInteractionChanged(outputNode);
                            }
                        }
                    }
                }
                
                RefreshQuestWarnings();
            }

            return change;
        }
        
        public void SetPanMode(bool isPanMode)
        {
            m_isPanMode = isPanMode;
            
            this.RemoveManipulator(m_contentDragger);
            
            if (isPanMode)
            {
                m_contentDragger = new LeftMousePanManipulator();
                
            }
            else
            {
                m_contentDragger = new ContentDragger();
                
                this.AddManipulator(m_selectionDragger);
                this.AddManipulator(m_rectangleSelector);
            }
            
            this.AddManipulator(m_contentDragger);
        }
        
        public void Cleanup()
        {
            EditorApplication.update -= OnEditorUpdate;
            Undo.undoRedoPerformed -= OnUndoRedoPerformed;
        }
        
        private void OnUndoRedoPerformed()
        {
            
            EditorApplication.delayCall += () =>
            {
                if (m_quest == null || m_graphData == null)
                    return;
                
                var savedPosition = new Vector3(contentViewContainer.resolvedStyle.translate.x, contentViewContainer.resolvedStyle.translate.y, 0);
                var savedScale = contentViewContainer.resolvedStyle.scale;
                
                LoadGraph(m_graphData);
                
                UpdateViewTransform(savedPosition, new Vector3(savedScale.value.x, savedScale.value.y, 1));
            };
        }
        
        public bool IsPanMode() => m_isPanMode;
        
        private float m_lastUpdateTime = 0f;
        private void OnEditorUpdate()
        {
            if (EditorApplication.timeSinceStartup - m_lastUpdateTime < 2.0f)
                return;
                
            m_lastUpdateTime = (float)EditorApplication.timeSinceStartup;
            
            if (m_quest == null)
                return;
            
            var nodesCopy = m_nodeViews.ToList();
            foreach (var kvp in nodesCopy)
            {
                var node = kvp.Value;
                if (node.userData is System.Tuple<QuestTask, int> taskData)
                {
                    var task = taskData.Item1;
                    if (task == null)
                        continue;
                    
                    var taskIndex = taskData.Item2;
                    
                    string newTitle = GetTaskNodeTitle(task, taskIndex);
                    if (node.title != newTitle)
                    {
                        node.title = newTitle;
                    }
                    
                    var nameLabel = node.Q<Label>("task-name-label");
                    if (nameLabel != null && nameLabel.text != task.name)
                    {
                        nameLabel.text = task.name;
                    }
                    
                    var descLabel = node.extensionContainer.Q<Label>("task-description");
                    if (descLabel != null)
                    {
                        string newDesc = GetTaskNodeDescription(task);
                        if (newDesc.Length > 150)
                        {
                            newDesc = newDesc.Substring(0, 147) + "...";
                        }
                        if (descLabel.text != newDesc)
                        {
                            descLabel.text = newDesc;
                        }
                    }
                }
                else if (node.userData is DialogueSequence dialogue)
                {
                    if (dialogue == null)
                        continue;
                    
                    var nameLabel = node.Q<Label>("dialogue-name-label");
                    if (nameLabel != null && nameLabel.text != dialogue.name)
                    {
                        nameLabel.text = dialogue.name;
                    }
                }
                else if (node.name == "quest-info")
                {
                    var nameLabel = node.Q<Label>("quest-name-label");
                    if (nameLabel != null && nameLabel.text != m_quest.name)
                    {
                        nameLabel.text = m_quest.name;
                    }
                }
            }
            
            UpdateQuestWarningIndicator();
        }
        
        private void UpdateQuestWarningIndicator()
        {
            bool hasWarnings = false;
            
            foreach (var kvp in m_nodeViews)
            {
                var node = kvp.Value;
                
                if (node.userData is System.Tuple<QuestTask, int> taskData)
                {
                    if (taskData.Item1 == null)
                        continue;
                    
                    string title = GetTaskNodeTitle(taskData.Item1, taskData.Item2);
                    if (title.Contains("(unconfigured)"))
                    {
                        hasWarnings = true;
                        break;
                    }
                }
                else if (node.userData is DialogueSequence dialogue)
                {
                    if (dialogue == null)
                        continue;
                    
                    if (dialogue.lines == null || dialogue.lines.Length == 0)
                    {
                        hasWarnings = true;
                        break;
                    }
                }
            }
            
            m_editorWindow?.UpdateWarningIndicator(hasWarnings);
        }
        
        private void RefreshQuestWarnings()
        {
            UpdateQuestWarningIndicator();
        }
        
        private void OnKeyDown(KeyDownEvent evt)
        {
            var target = evt.target as VisualElement;
            if (target != null)
            {
                var currentElement = target;
                while (currentElement != null)
                {
                    if (currentElement is TextField)
                    {
                        return;
                    }
                    currentElement = currentElement.parent;
                }
            }
            
            bool isMac = Application.platform == RuntimePlatform.OSXEditor;
            bool cmdCtrl = isMac ? evt.commandKey : evt.ctrlKey;

            if (cmdCtrl && evt.keyCode == KeyCode.Z && !evt.shiftKey)
            {
                Undo.PerformUndo();
                evt.StopPropagation();
                return;
            }
            else if (cmdCtrl && (evt.keyCode == KeyCode.Y || (evt.keyCode == KeyCode.Z && evt.shiftKey)))
            {
                Undo.PerformRedo();
                evt.StopPropagation();
                return;
            }
            else if (cmdCtrl && evt.keyCode == KeyCode.C)
            {
                return;
            }
            else if (cmdCtrl && evt.keyCode == KeyCode.V)
            {
                return;
            }
            else if (cmdCtrl && evt.keyCode == KeyCode.X)
            {
                return;
            }
            else if (evt.keyCode == KeyCode.F && selection.Count > 0)
            {
                FrameSelection();
                evt.StopPropagation();
                return;
            }
            else if (cmdCtrl && evt.keyCode == KeyCode.A)
            {
                ClearSelection();
                foreach (var node in nodes.ToList())
                {
                    AddToSelection(node);
                }
                evt.StopPropagation();
                return;
            }
            else if (evt.keyCode == KeyCode.Delete || evt.keyCode == KeyCode.Backspace)
            {
                if (selection.Count > 0)
                {
                    deleteSelection?.Invoke("Delete", AskUser.DontAskUser);
                }
                evt.StopPropagation();
                return;
            }
        }

        public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
        {
            base.BuildContextualMenu(evt);
            
            if (evt.target is GraphView)
            {
                Vector2 mousePosition = viewTransform.matrix.inverse.MultiplyPoint(evt.localMousePosition);
                
                evt.menu.InsertAction(0, "Create Task/Talk to NPC", (action) =>
                {
                    CreateNewTask(typeof(Gyvr.Mythril2D.TalkToNPCTask), mousePosition);
                });
                
                evt.menu.InsertAction(1, "Create Task/Kill Monster", (action) =>
                {
                    CreateNewTask(typeof(Gyvr.Mythril2D.KillMonsterTask), mousePosition);
                });
                
                evt.menu.InsertAction(2, "Create Task/Collect Item", (action) =>
                {
                    CreateNewTask(typeof(Gyvr.Mythril2D.ItemTask), mousePosition);
                });
                
                evt.menu.InsertAction(3, "Create Task/Game Flag", (action) =>
                {
                    CreateNewTask(typeof(Gyvr.Mythril2D.GameFlagTask), mousePosition);
                });
                
                evt.menu.InsertSeparator("", 4);
                
                evt.menu.InsertAction(5, "Add Dialogue", (action) =>
                {
                    CreateNewDialogue(mousePosition);
                });
                
                evt.menu.InsertAction(6, "Add Hint Override", (action) =>
                {
                    CreateNewHintOverride(mousePosition);
                });
                
                evt.menu.InsertAction(7, "Add Conditional Interaction", (action) =>
                {
                    CreateConditionalNPCInteractionNode(mousePosition);
                });
                
                evt.menu.InsertAction(8, "Add Sticky Note", (action) =>
                {
                    CreateStickyNote(mousePosition);
                });
            }
            
            if (selection.Count > 0)
            {
                bool hasRemovableNodes = false;
                foreach (var selected in selection)
                {
                    if (selected is Node node && node.title != "Quest Info")
                    {
                        string nodeId = node.viewDataKey;
                        if (nodeId != null && (nodeId.StartsWith("dialogue-") || nodeId.StartsWith("hint-") || nodeId.StartsWith("sticky-")))
                        {
                            hasRemovableNodes = true;
                            break;
                        }
                    }
                }
                
                if (hasRemovableNodes)
                {
                    evt.menu.AppendSeparator();
                    evt.menu.AppendAction("Remove From Graph", (action) =>
                    {
                        RemoveSelectedFromGraph();
                    });
                }
            }
        }
        
        private void RemoveSelectedFromGraph()
        {
            Undo.RecordObject(m_graphData, "Remove From Graph");
            
            var nodesToRemove = new List<Node>();
            var edgesToRemove = new List<Edge>();
            
            foreach (var selected in selection)
            {
                if (selected is Node node && node.title != "Quest Info")
                {
                    string nodeId = node.viewDataKey;
                    if (nodeId != null && (nodeId.StartsWith("dialogue-") || nodeId.StartsWith("hint-") || nodeId.StartsWith("sticky-")))
                    {
                        nodesToRemove.Add(node);
                        
                        foreach (var port in node.inputContainer.Children().OfType<Port>())
                        {
                            edgesToRemove.AddRange(port.connections);
                        }
                        foreach (var port in node.outputContainer.Children().OfType<Port>())
                        {
                            edgesToRemove.AddRange(port.connections);
                        }
                    }
                }
            }
            
            foreach (var edge in edgesToRemove.Distinct())
            {
                edge.input?.Disconnect(edge);
                edge.output?.Disconnect(edge);
                RemoveElement(edge);
            }
            
            foreach (var node in nodesToRemove)
            {
                string nodeId = node.viewDataKey;
                
                if (m_graphData != null && m_graphData.nodes != null)
                {
                    m_graphData.nodes.RemoveAll(n => n.nodeId == nodeId);
                }
                
                if (m_graphData != null && m_graphData.connections != null)
                {
                    m_graphData.connections.RemoveAll(c => c.fromNodeId == nodeId || c.toNodeId == nodeId);
                }
                
                RemoveElement(node);
            }
            
            SaveGraphData();
            
        }

        private void CreateNewTask(System.Type taskType, Vector2 position)
        {
            if (m_quest == null || m_quest.tasks == null)
                return;

            Undo.RecordObject(m_quest, "Create Task");
            Undo.RecordObject(m_graphData, "Create Task");

            var newTask = ScriptableObject.CreateInstance(taskType) as Gyvr.Mythril2D.QuestTask;
            if (newTask == null)
                return;

            string questPath = AssetDatabase.GetAssetPath(m_quest);
            string questName = System.IO.Path.GetFileNameWithoutExtension(questPath);
            
            if (questName.StartsWith("QUEST_"))
                questName = questName.Substring(6);
            
            string taskTypeName = taskType.Name.Replace("Task", "");
            
            // Generate unique task name with counter
            string baseTaskName = $"TASK_{questName}_{taskTypeName}";
            int taskCount = 1;
            string finalTaskName = $"{baseTaskName}{taskCount}";
            
            // Check existing tasks for name conflicts
            var existingNames = new HashSet<string>();
            if (m_quest.tasks != null)
            {
                foreach (var existingTask in m_quest.tasks)
                {
                    if (existingTask != null)
                        existingNames.Add(existingTask.name);
                }
            }
            
            while (existingNames.Contains(finalTaskName))
            {
                taskCount++;
                finalTaskName = $"{baseTaskName}{taskCount}";
            }
            
            newTask.name = finalTaskName;
            
            AssetDatabase.AddObjectToAsset(newTask, questPath);

            var tasksArray = m_quest.tasks;
            System.Array.Resize(ref tasksArray, tasksArray.Length + 1);
            tasksArray[tasksArray.Length - 1] = newTask;
            
            AssetDatabase.SaveAssets();
            
            var serializedQuest = new SerializedObject(m_quest);
            var tasksProperty = serializedQuest.FindProperty("m_tasks");
            tasksProperty.arraySize = tasksArray.Length;
            tasksProperty.GetArrayElementAtIndex(tasksArray.Length - 1).objectReferenceValue = newTask;
            serializedQuest.ApplyModifiedProperties();
            
            AssetDatabase.SaveAssets();

            var node = CreateTaskNode(tasksArray.Length - 1, position);
            
            RefreshQuestWarnings();
            
        }

        private void CreateNewDialogue(Vector2 position)
        {
            if (m_quest == null)
                return;

            Undo.RecordObject(m_quest, "Create Dialogue");
            Undo.RecordObject(m_graphData, "Create Dialogue");

            string questPath = AssetDatabase.GetAssetPath(m_quest);
            string questName = System.IO.Path.GetFileNameWithoutExtension(questPath);
            
            if (questName.StartsWith("QUEST_"))
                questName = questName.Substring(6);
            
            var newDialogue = ScriptableObject.CreateInstance<Gyvr.Mythril2D.DialogueSequence>();
            
            newDialogue.lines = new string[] { "[Click here to edit]" };
            
            // Get existing sub-asset names
            var existingDialogueNames = new HashSet<string>();
            var subAssets = AssetDatabase.LoadAllAssetsAtPath(questPath);
            foreach (var subAsset in subAssets)
            {
                if (subAsset is Gyvr.Mythril2D.DialogueSequence)
                    existingDialogueNames.Add(subAsset.name);
            }
            
            int dialogueCount = 1;
            string dialogueName;
            do
            {
                dialogueName = $"DIAL_{questName}_{dialogueCount}";
                dialogueCount++;
            }
            while (existingDialogueNames.Contains(dialogueName));
            
            newDialogue.name = dialogueName;
            
            AssetDatabase.AddObjectToAsset(newDialogue, questPath);
            AssetDatabase.SaveAssets();

            var node = CreateDialogueNode(newDialogue, "Dialogue", position);
            
            RefreshQuestWarnings();
            
        }

        private void CreateNewHintOverride(Vector2 position)
        {
            if (m_quest == null)
                return;

            Undo.RecordObject(m_quest, "Create Hint Override");
            Undo.RecordObject(m_graphData, "Create Hint Override");

            string questPath = AssetDatabase.GetAssetPath(m_quest);
            string questName = System.IO.Path.GetFileNameWithoutExtension(questPath);
            
            if (questName.StartsWith("QUEST_"))
                questName = questName.Substring(6);
            
            var newDialogue = ScriptableObject.CreateInstance<Gyvr.Mythril2D.DialogueSequence>();
            
            newDialogue.lines = new string[] { "[Click here to edit]" };
            
            // Get existing sub-asset names
            var existingDialogueNames = new HashSet<string>();
            var subAssets = AssetDatabase.LoadAllAssetsAtPath(questPath);
            foreach (var subAsset in subAssets)
            {
                if (subAsset is Gyvr.Mythril2D.DialogueSequence)
                    existingDialogueNames.Add(subAsset.name);
            }
            
            int hintCount = 1;
            string dialogueName;
            do
            {
                dialogueName = $"DIAL_{questName}_HintOverride{hintCount}";
                hintCount++;
            }
            while (existingDialogueNames.Contains(dialogueName));
            
            newDialogue.name = dialogueName;
            
            AssetDatabase.AddObjectToAsset(newDialogue, questPath);
            AssetDatabase.SaveAssets();

            var node = CreateDialogueNode(newDialogue, "Hint Override", position, role: "HintOverride_Unconnected");
            
            RefreshQuestWarnings();
            
        }

        private void CreateConditionalNPCInteractionNode(Vector2 position)
        {
            if (m_quest == null)
                return;

            Undo.RecordObject(m_graphData, "Create Conditional NPC Interaction");

            var node = CreateConditionalNPCInteractionNodeInternal(null, null, null, position);
            
            if (node != null)
            {
                AddElement(node);
                m_nodeViews[node.name] = node;
                SaveGraphData();
            }
            
            RefreshQuestWarnings();
        }

        private Node CreateConditionalNPCInteractionNodeInternal(
            GameObject npcPrefab, 
            Gyvr.Mythril2D.QuestTask task, 
            Gyvr.Mythril2D.DialogueSequence dialogue, 
            Vector2 position, 
            string nodeId = null, 
            string interactionType = "DialogueInteraction",
            string conditionType = "IsQuestTaskActive",
            Gyvr.Mythril2D.Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "",
            Gyvr.Mythril2D.Quest interactionQuest = null,
            string commandType = "SetGameFlag",
            string interruptionPolicy = "OnSuccess",
            ConditionalNPCInteractionData commandData = null)
        {
            var node = new Node();
            node.title = "Conditional Interaction";
            node.name = nodeId ?? $"conditional-npc-{System.Guid.NewGuid().ToString().Substring(0, 8)}";
            node.viewDataKey = node.name;
            
            node.capabilities &= ~Capabilities.Collapsible;
            
            var titleButtonsContainer = node.Q("title-button-container");
            if (titleButtonsContainer != null)
                titleButtonsContainer.style.display = DisplayStyle.None;

            var userData = new ConditionalNPCInteractionData
            {
                npcPrefab = npcPrefab,
                task = task,
                dialogue = dialogue,
                interactionType = interactionType,
                conditionType = conditionType,
                conditionQuest = conditionQuest,
                conditionQuestState = conditionQuestState,
                conditionGameFlag = conditionGameFlag,
                interactionQuest = interactionQuest,
                commandType = commandType,
                interruptionPolicy = interruptionPolicy
            };
            
            // Copy command-specific data if provided
            if (commandData != null)
            {
                userData.commandFlagId = commandData.commandFlagId;
                userData.commandFlagState = commandData.commandFlagState;
                userData.commandItem = commandData.commandItem;
                userData.commandQuantity = commandData.commandQuantity;
                userData.commandIsAdd = commandData.commandIsAdd;
                userData.commandMoneyAmount = commandData.commandMoneyAmount;
                userData.commandExperience = commandData.commandExperience;
                userData.commandTask = commandData.commandTask;
                userData.commandQuest = commandData.commandQuest;
                userData.commandDialogue = commandData.commandDialogue;
                userData.commandWaitTime = commandData.commandWaitTime;
            }
            
            node.userData = userData;

            node.SetPosition(new Rect(position, Vector2.zero));
            
            node.mainContainer.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 1f);
            node.mainContainer.style.maxWidth = 250;
            node.mainContainer.style.minWidth = 250;
            
            node.style.borderBottomWidth = 0;
            node.style.borderLeftWidth = 0;
            node.style.borderRightWidth = 0;
            node.style.borderTopWidth = 0;
            node.style.paddingBottom = 0;
            node.style.paddingLeft = 0;
            node.style.paddingRight = 0;
            node.style.paddingTop = 0;
            node.style.marginTop = 1;
            node.style.marginBottom = 1;
            node.style.marginLeft = 1;
            node.style.marginRight = 1;
            node.style.overflow = Overflow.Hidden;
            
            node.titleContainer.style.backgroundColor = new Color(0.8f, 0.3f, 0.3f, 0.5f);
            node.titleContainer.style.paddingLeft = 18;
            
            var titleLabel = node.titleContainer.Q<Label>();
            if (titleLabel != null && EditorFont != null)
            {
                titleLabel.style.unityFont = EditorFont;
            }

            var npcLabel = new Label("NPC:")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 6,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            node.mainContainer.Add(npcLabel);
            
            var npcField = new UnityEditor.UIElements.ObjectField();
            npcField.objectType = typeof(GameObject);
            npcField.allowSceneObjects = false;
            npcField.value = npcPrefab;
            npcField.name = "npc-field";
            npcField.style.marginLeft = 8;
            npcField.style.marginRight = 8;
            npcField.style.marginBottom = 4;
            npcField.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(npcField);
            
            var separator = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 4,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(separator);
            
            var conditionLabel = new Label("Condition:")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            if (EditorFont != null) conditionLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(conditionLabel);
            
            var conditionTypes = new List<string> { "IsQuestTaskActive", "IsQuestInState", "IsQuestTaskInState", "IsGameFlagSet" };
            int conditionIndex = conditionTypes.IndexOf(conditionType);
            if (conditionIndex < 0) conditionIndex = 0;
            var conditionDropdown = new PopupField<string>(conditionTypes, conditionIndex);
            conditionDropdown.name = "condition-type-dropdown";
            conditionDropdown.style.marginLeft = 8;
            conditionDropdown.style.marginRight = 8;
            conditionDropdown.style.marginBottom = 4;
            conditionDropdown.style.height = 18;
            conditionDropdown.RegisterValueChangedCallback(evt => OnConditionTypeChanged(node));
            node.mainContainer.Add(conditionDropdown);
            
            var taskLabel = new Label("Task:")
            {
                name = "task-label",
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    display = (conditionType == "IsQuestTaskActive" || conditionType == "IsQuestTaskInState") ? DisplayStyle.Flex : DisplayStyle.None
                }
            };
            if (EditorFont != null) taskLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(taskLabel);
            
            var taskChoices = GetQuestTasksForDropdown();
            string initialTask = task != null ? task.name : (taskChoices.Count > 0 ? taskChoices[0] : "None");
            if (!taskChoices.Contains(initialTask) && taskChoices.Count > 0)
                initialTask = taskChoices[0];
            var taskDropdown = new PopupField<string>(taskChoices, taskChoices.IndexOf(initialTask) >= 0 ? taskChoices.IndexOf(initialTask) : 0);
            taskDropdown.name = "task-dropdown";
            taskDropdown.style.marginLeft = 8;
            taskDropdown.style.marginRight = 8;
            taskDropdown.style.marginBottom = 4;
            taskDropdown.style.height = 18;
            taskDropdown.style.display = (conditionType == "IsQuestTaskActive" || conditionType == "IsQuestTaskInState") ? DisplayStyle.Flex : DisplayStyle.None;
            taskDropdown.RegisterCallback<MouseDownEvent>(evt => RefreshTaskDropdownChoices(taskDropdown));
            taskDropdown.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(taskDropdown);
            
            var questLabel = new Label("Quest:")
            {
                name = "quest-label",
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    display = conditionType == "IsQuestInState" ? DisplayStyle.Flex : DisplayStyle.None
                }
            };
            if (EditorFont != null) questLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(questLabel);
            
            var questField = new UnityEditor.UIElements.ObjectField();
            questField.objectType = typeof(Gyvr.Mythril2D.Quest);
            questField.allowSceneObjects = false;
            questField.value = (node.userData as ConditionalNPCInteractionData)?.conditionQuest;
            questField.name = "quest-field";
            questField.style.marginLeft = 8;
            questField.style.marginRight = 8;
            questField.style.marginBottom = 4;
            questField.style.display = conditionType == "IsQuestInState" ? DisplayStyle.Flex : DisplayStyle.None;
            questField.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(questField);
            
            var questStateLabel = new Label("State:")
            {
                name = "quest-state-label",
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    display = (conditionType == "IsQuestInState" || conditionType == "IsQuestTaskInState") ? DisplayStyle.Flex : DisplayStyle.None
                }
            };
            if (EditorFont != null) questStateLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(questStateLabel);
            
            var questStates = new List<string> { "Unlocked", "Available", "Active", "Fullfilled", "Completed" };
            string currentState = (node.userData as ConditionalNPCInteractionData)?.conditionQuestState ?? "Active";
            int stateIndex = questStates.IndexOf(currentState);
            if (stateIndex < 0) stateIndex = 2;
            var questStateDropdown = new PopupField<string>(questStates, stateIndex);
            questStateDropdown.name = "quest-state-dropdown";
            questStateDropdown.style.marginLeft = 8;
            questStateDropdown.style.marginRight = 8;
            questStateDropdown.style.marginBottom = 4;
            questStateDropdown.style.height = 18;
            questStateDropdown.style.display = (conditionType == "IsQuestInState" || conditionType == "IsQuestTaskInState") ? DisplayStyle.Flex : DisplayStyle.None;
            questStateDropdown.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(questStateDropdown);
            
            var gameFlagLabel = new Label("Game Flag:")
            {
                name = "game-flag-label",
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    display = conditionType == "IsGameFlagSet" ? DisplayStyle.Flex : DisplayStyle.None
                }
            };
            if (EditorFont != null) gameFlagLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(gameFlagLabel);
            
            var gameFlagField = new TextField();
            gameFlagField.value = (node.userData as ConditionalNPCInteractionData)?.conditionGameFlag ?? "";
            gameFlagField.name = "game-flag-field";
            gameFlagField.style.marginLeft = 8;
            gameFlagField.style.marginRight = 8;
            gameFlagField.style.marginBottom = 4;
            gameFlagField.style.display = conditionType == "IsGameFlagSet" ? DisplayStyle.Flex : DisplayStyle.None;
            gameFlagField.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(gameFlagField);
            
            var separator1b = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 4,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(separator1b);
            
            var typeLabel = new Label("Interaction:")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            if (EditorFont != null) typeLabel.style.unityFont = EditorFont;
            node.mainContainer.Add(typeLabel);
            
            var interactionTypes = new List<string> { "DialogueInteraction", "CommandInteraction", "SequentialInteraction" };
            int typeIndex = interactionTypes.IndexOf(interactionType);
            if (typeIndex < 0) typeIndex = 0;
            var typeDropdown = new PopupField<string>(interactionTypes, typeIndex);
            typeDropdown.name = "interaction-type-dropdown";
            typeDropdown.style.marginLeft = 8;
            typeDropdown.style.marginRight = 8;
            typeDropdown.style.marginBottom = 4;
            typeDropdown.style.height = 18;
            typeDropdown.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            node.mainContainer.Add(typeDropdown);
            
            bool isDialogueInteraction = (interactionType == "DialogueInteraction");
            bool isCommandInteraction = (interactionType == "CommandInteraction");
            bool isSequentialInteraction = (interactionType == "SequentialInteraction");
            
            // Command options container
            var commandOptionsContainer = new VisualElement
            {
                name = "command-options-container",
                style = { display = isCommandInteraction ? DisplayStyle.Flex : DisplayStyle.None }
            };
            
            var commandLabel = new Label("Command:")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            if (EditorFont != null) commandLabel.style.unityFont = EditorFont;
            commandOptionsContainer.Add(commandLabel);
            
            var commandTypes = new List<string> { 
                "SetGameFlag", "AddOrRemoveItem", "AddOrRemoveMoney", "AddExperience", 
                "PlayDialogueLine", "PlayDialogueSequence", "CompleteTask", "UnlockQuest",
                "OpenShopMenu", "OpenCraftMenu", "OpenMenu", "CloseMenus",
                "PlayAudioClip", "Wait", "DestroyEntity", "MoveCamera"
            };
            int cmdIndex = commandTypes.IndexOf(commandType);
            if (cmdIndex < 0) cmdIndex = 0;
            var commandDropdown = new PopupField<string>(commandTypes, cmdIndex);
            commandDropdown.name = "command-type-dropdown";
            commandDropdown.style.marginLeft = 8;
            commandDropdown.style.marginRight = 8;
            commandDropdown.style.marginBottom = 4;
            commandDropdown.style.height = 18;
            commandDropdown.RegisterValueChangedCallback(evt => {
                UpdateCommandFieldsVisibility(node, evt.newValue);
                OnConditionalNPCInteractionChanged(node);
            });
            commandOptionsContainer.Add(commandDropdown);
            
            // Command-specific fields container
            var commandFieldsContainer = new VisualElement { name = "command-fields-container" };
            commandOptionsContainer.Add(commandFieldsContainer);
            
            // Add command-specific fields
            AddCommandSpecificFields(node, commandFieldsContainer, commandType);
            
            node.mainContainer.Add(commandOptionsContainer);
            
            // Sequential options container
            var sequentialOptionsContainer = new VisualElement
            {
                name = "sequential-options-container",
                style = { display = isSequentialInteraction ? DisplayStyle.Flex : DisplayStyle.None }
            };
            
            var policyLabel = new Label("Interruption Policy:")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            if (EditorFont != null) policyLabel.style.unityFont = EditorFont;
            sequentialOptionsContainer.Add(policyLabel);
            
            var policies = new List<string> { "OnSuccess", "OnFailure", "Never" };
            int policyIndex = policies.IndexOf(interruptionPolicy);
            if (policyIndex < 0) policyIndex = 0;
            var policyDropdown = new PopupField<string>(policies, policyIndex);
            policyDropdown.name = "interruption-policy-dropdown";
            policyDropdown.style.marginLeft = 8;
            policyDropdown.style.marginRight = 8;
            policyDropdown.style.marginBottom = 4;
            policyDropdown.style.height = 18;
            policyDropdown.RegisterValueChangedCallback(evt => OnConditionalNPCInteractionChanged(node));
            sequentialOptionsContainer.Add(policyDropdown);
            
            var sequentialHintLabel = new Label("⚠ Configure interactions in NPC Inspector")
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingBottom = 4,
                    paddingTop = 2,
                    unityFontStyleAndWeight = FontStyle.Italic,
                    color = new Color(1f, 0.9f, 0.5f)
                }
            };
            sequentialOptionsContainer.Add(sequentialHintLabel);
            node.mainContainer.Add(sequentialOptionsContainer);
            
            var wireHintLabel = new Label("⚠ Wire Dialogue via output →")
            {
                name = "wire-hint-label",
                style = {
                    fontSize = 7,
                    opacity = 0.7f,
                    paddingLeft = 8,
                    paddingBottom = 4,
                    paddingTop = 2,
                    unityFontStyleAndWeight = FontStyle.Normal,
                    color = new Color(1f, 1f, 1f),
                    display = isDialogueInteraction ? DisplayStyle.Flex : DisplayStyle.None
                }
            };
            node.mainContainer.Add(wireHintLabel);
            
            var separator2 = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 4,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(separator2);
            
            string assetName = npcPrefab != null ? $"→ {npcPrefab.name}" : "(select NPC)";
            var nameLabel = new Label(assetName)
            {
                name = "npc-name-label",
                style = {
                    fontSize = 7,
                    opacity = 0.5f,
                    paddingTop = 2,
                    paddingBottom = 4,
                    paddingLeft = 8,
                    minWidth = 200,
                    unityFontStyleAndWeight = FontStyle.Italic
                }
            };
            node.mainContainer.Add(nameLabel);
            
            node.inputContainer.style.display = DisplayStyle.None;
            node.outputContainer.style.display = DisplayStyle.None;
            
            var inputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(bool));
            inputPort.portName = "";
            inputPort.portColor = new Color(0.6f, 0.15f, 0.15f);
            inputPort.style.position = Position.Absolute;
            inputPort.style.left = 2;
            inputPort.style.top = 10;
            node.Add(inputPort);

            var outputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(bool));
            outputPort.portName = "";
            outputPort.portColor = new Color(0.6f, 0.15f, 0.15f);
            outputPort.style.position = Position.Absolute;
            outputPort.style.right = 2;
            outputPort.style.top = 10;
            outputPort.style.display = isDialogueInteraction ? DisplayStyle.Flex : DisplayStyle.None;
            node.Add(outputPort);

            AddElement(node);
            m_nodeViews[node.name] = node;
            
            node.RefreshExpandedState();
            node.RefreshPorts();

            SaveConditionalNPCInteractionNodeData(node.name, node.userData as ConditionalNPCInteractionData, position);
            
            node.RegisterCallback<GeometryChangedEvent>(evt =>
            {
                if (m_graphData != null)
                {
                    var nodeData = m_graphData.nodes.FirstOrDefault(n => n.nodeId == node.name);
                    if (nodeData != null)
                    {
                        var newPos = node.GetPosition().position;
                        if (Vector2.Distance(nodeData.position, newPos) > 1f)
                        {
                            nodeData.position = newPos;
                            EditorUtility.SetDirty(m_graphData);
                        }
                    }
                }
            });

            return node;
        }
        
        private void OnConditionalNPCInteractionChanged(Node node)
        {
            if (!(node.userData is ConditionalNPCInteractionData data)) return;
            
            var npcField = node.Q<UnityEditor.UIElements.ObjectField>("npc-field");
            var newNPC = npcField?.value as GameObject;
            
            if (newNPC != null && newNPC.GetComponent<Gyvr.Mythril2D.NPC>() == null)
            {
                if (npcField != null) npcField.value = data.npcPrefab;
                return;
            }
            
            data.npcPrefab = newNPC;
            
            var nameLabel = node.Q<Label>("npc-name-label");
            if (nameLabel != null)
            {
                nameLabel.text = newNPC != null ? $"→ {newNPC.name}" : "(select NPC)";
            }
            
            var conditionDropdown = node.Q<PopupField<string>>("condition-type-dropdown");
            data.conditionType = conditionDropdown?.value ?? "IsQuestTaskActive";
            
            var taskDropdown = node.Q<PopupField<string>>("task-dropdown");
            if (taskDropdown != null && !string.IsNullOrEmpty(taskDropdown.value))
            {
                data.task = FindQuestTaskByName(taskDropdown.value);
            }
            
            var questField = node.Q<UnityEditor.UIElements.ObjectField>("quest-field");
            if (questField != null)
            {
                data.conditionQuest = questField.value as Gyvr.Mythril2D.Quest;
            }
            
            var questStateDropdown = node.Q<PopupField<string>>("quest-state-dropdown");
            if (questStateDropdown != null)
            {
                data.conditionQuestState = questStateDropdown.value ?? "Active";
            }
            
            var gameFlagField = node.Q<TextField>("game-flag-field");
            if (gameFlagField != null)
            {
                data.conditionGameFlag = gameFlagField.value ?? "";
            }
            
            var typeDropdown = node.Q<PopupField<string>>("interaction-type-dropdown");
            string interactionType = typeDropdown?.value ?? "DialogueInteraction";
            data.interactionType = interactionType;
            
            // Read command type
            var commandDropdown = node.Q<PopupField<string>>("command-type-dropdown");
            if (commandDropdown != null)
            {
                data.commandType = commandDropdown.value ?? "SetGameFlag";
            }
            
            // Read interruption policy
            var policyDropdown = node.Q<PopupField<string>>("interruption-policy-dropdown");
            if (policyDropdown != null)
            {
                data.interruptionPolicy = policyDropdown.value ?? "OnSuccess";
            }
            
            var wireHintLabel = node.Q<Label>("wire-hint-label");
            var outputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
            
            bool isDialogueInteraction = (interactionType == "DialogueInteraction");
            bool isCommandInteraction = (interactionType == "CommandInteraction");
            bool isSequentialInteraction = (interactionType == "SequentialInteraction");
            bool isOutputConnected = (outputPort != null && outputPort.connected);
            
            // If switching away from DialogueInteraction, disconnect any wired edges
            if (!isDialogueInteraction && outputPort != null && outputPort.connected)
            {
                var edgesToRemove = outputPort.connections.ToList();
                foreach (var edge in edgesToRemove)
                {
                    edge.input?.Disconnect(edge);
                    edge.output?.Disconnect(edge);
                    RemoveElement(edge);
                }
            }
            
            // Toggle command options visibility
            var commandOptionsContainer = node.Q<VisualElement>("command-options-container");
            if (commandOptionsContainer != null)
            {
                commandOptionsContainer.style.display = isCommandInteraction ? DisplayStyle.Flex : DisplayStyle.None;
            }
            
            // Toggle sequential options visibility
            var sequentialOptionsContainer = node.Q<VisualElement>("sequential-options-container");
            if (sequentialOptionsContainer != null)
            {
                sequentialOptionsContainer.style.display = isSequentialInteraction ? DisplayStyle.Flex : DisplayStyle.None;
            }
            
            if (wireHintLabel != null)
            {
                if (isDialogueInteraction && !isOutputConnected)
                {
                    wireHintLabel.style.display = DisplayStyle.Flex;
                    wireHintLabel.text = "⚠ Wire Dialogue via output →";
                }
                else
                {
                    wireHintLabel.style.display = DisplayStyle.None;
                }
            }
            
            if (outputPort != null)
                outputPort.style.display = isDialogueInteraction ? DisplayStyle.Flex : DisplayStyle.None;
            
            Gyvr.Mythril2D.DialogueSequence selectedDialogue = null;
            
            if (isDialogueInteraction)
            {
                if (outputPort != null && outputPort.connected)
                {
                    var edge = outputPort.connections.FirstOrDefault();
                    if (edge?.input?.node?.userData is Gyvr.Mythril2D.DialogueSequence dialogue)
                    {
                        selectedDialogue = dialogue;
                    }
                }
            }
            
            data.dialogue = selectedDialogue;
            
            UpdateNPCInteractionStatusLabel(node, data);
            
            bool hasRequiredConditionData = false;
            switch (data.conditionType)
            {
                case "IsQuestTaskActive":
                case "IsQuestTaskInState":
                    hasRequiredConditionData = (data.task != null);
                    break;
                case "IsQuestInState":
                    hasRequiredConditionData = (data.conditionQuest != null);
                    break;
                case "IsGameFlagSet":
                    hasRequiredConditionData = !string.IsNullOrEmpty(data.conditionGameFlag);
                    break;
                default:
                    hasRequiredConditionData = (data.task != null);
                    break;
            }
            
            if (data.npcPrefab != null && hasRequiredConditionData)
            {
                bool success = NPCInteractionManager.AddOrUpdateConditionalInteraction(
                    data.npcPrefab, 
                    data.task, 
                    interactionType, 
                    data.dialogue,
                    data.conditionType,
                    data.conditionQuest,
                    data.conditionQuestState,
                    data.conditionGameFlag,
                    data.interactionQuest);
                if (success)
                {
                    data.isApplied = true;
                }
            }
            
            SaveConditionalNPCInteractionNodeData(node.name, data, node.GetPosition().position);
            SaveGraphData();
        }

        private void UpdateCommandFieldsVisibility(Node node, string commandType)
        {
            var commandFieldsContainer = node.Q<VisualElement>("command-fields-container");
            if (commandFieldsContainer == null) return;
            
            commandFieldsContainer.Clear();
            AddCommandSpecificFields(node, commandFieldsContainer, commandType);
        }

        private void AddCommandSpecificFields(Node node, VisualElement container, string commandType)
        {
            var data = node.userData as ConditionalNPCInteractionData;
            if (data == null) return;
            
            switch (commandType)
            {
                case "SetGameFlag":
                    AddCommandField(container, "Flag ID:", () => {
                        var field = new TextField { value = data.commandFlagId ?? "" };
                        field.name = "command-flag-id";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandFlagId = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    AddCommandField(container, "Set To:", () => {
                        var field = new Toggle { value = data.commandFlagState };
                        field.name = "command-flag-state";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandFlagState = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "AddOrRemoveItem":
                    AddCommandField(container, "Action:", () => {
                        var choices = new List<string> { "Add", "Remove" };
                        var field = new PopupField<string>(choices, data.commandIsAdd ? 0 : 1);
                        field.name = "command-item-action";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandIsAdd = (evt.newValue == "Add");
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    AddCommandField(container, "Item:", () => {
                        var field = new UnityEditor.UIElements.ObjectField { 
                            objectType = typeof(Gyvr.Mythril2D.Item),
                            value = data.commandItem
                        };
                        field.name = "command-item";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandItem = evt.newValue as Gyvr.Mythril2D.Item;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    AddCommandField(container, "Quantity:", () => {
                        var field = new IntegerField { value = data.commandQuantity };
                        field.name = "command-quantity";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandQuantity = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "AddOrRemoveMoney":
                    AddCommandField(container, "Action:", () => {
                        var choices = new List<string> { "Add", "Remove" };
                        var field = new PopupField<string>(choices, data.commandIsAdd ? 0 : 1);
                        field.name = "command-money-action";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandIsAdd = (evt.newValue == "Add");
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    AddCommandField(container, "Amount:", () => {
                        var field = new IntegerField { value = data.commandMoneyAmount };
                        field.name = "command-money-amount";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandMoneyAmount = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "AddExperience":
                    AddCommandField(container, "Experience:", () => {
                        var field = new IntegerField { value = data.commandExperience };
                        field.name = "command-experience";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandExperience = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "CompleteTask":
                    AddCommandField(container, "Task:", () => {
                        var taskChoices = GetQuestTasksForDropdown();
                        int idx = 0;
                        if (data.commandTask != null)
                        {
                            idx = taskChoices.IndexOf(data.commandTask.name);
                            if (idx < 0) idx = 0;
                        }
                        var field = new PopupField<string>(taskChoices, idx);
                        field.name = "command-task";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandTask = FindQuestTaskByName(evt.newValue);
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "UnlockQuest":
                    AddCommandField(container, "Quest:", () => {
                        var field = new UnityEditor.UIElements.ObjectField { 
                            objectType = typeof(Gyvr.Mythril2D.Quest),
                            value = data.commandQuest
                        };
                        field.name = "command-quest";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandQuest = evt.newValue as Gyvr.Mythril2D.Quest;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "PlayDialogueSequence":
                    AddCommandField(container, "Dialogue:", () => {
                        var field = new UnityEditor.UIElements.ObjectField { 
                            objectType = typeof(Gyvr.Mythril2D.DialogueSequence),
                            value = data.commandDialogue
                        };
                        field.name = "command-dialogue";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandDialogue = evt.newValue as Gyvr.Mythril2D.DialogueSequence;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                case "Wait":
                    AddCommandField(container, "Duration (s):", () => {
                        var field = new FloatField { value = data.commandWaitTime };
                        field.name = "command-wait-time";
                        field.RegisterValueChangedCallback(evt => {
                            data.commandWaitTime = evt.newValue;
                            OnConditionalNPCInteractionChanged(node);
                        });
                        return field;
                    });
                    break;
                    
                default:
                    // For commands without configurable fields, show a hint
                    var hintLabel = new Label("(No additional configuration needed)")
                    {
                        style = {
                            fontSize = 7,
                            opacity = 0.5f,
                            paddingLeft = 8,
                            paddingBottom = 4,
                            paddingTop = 2,
                            unityFontStyleAndWeight = FontStyle.Italic
                        }
                    };
                    container.Add(hintLabel);
                    break;
            }
        }

        private void AddCommandField(VisualElement container, string label, System.Func<VisualElement> createField)
        {
            var labelElement = new Label(label)
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 2,
                    paddingBottom = 1
                }
            };
            container.Add(labelElement);
            
            var field = createField();
            field.style.marginLeft = 8;
            field.style.marginRight = 8;
            field.style.marginBottom = 2;
            field.style.height = 18;
            container.Add(field);
        }

        private void SaveConditionalNPCInteractionNodeData(string nodeId, ConditionalNPCInteractionData data, Vector2 position)
        {
            if (m_graphData == null) return;
            
            var metadata = new List<MetadataEntry>();
            
            metadata.Add(new MetadataEntry { key = "interactionType", value = data.interactionType ?? "DialogueInteraction" });
            metadata.Add(new MetadataEntry { key = "conditionType", value = data.conditionType ?? "IsQuestTaskActive" });
            metadata.Add(new MetadataEntry { key = "conditionQuestState", value = data.conditionQuestState ?? "Active" });
            metadata.Add(new MetadataEntry { key = "conditionGameFlag", value = data.conditionGameFlag ?? "" });
            metadata.Add(new MetadataEntry { key = "commandType", value = data.commandType ?? "SetGameFlag" });
            metadata.Add(new MetadataEntry { key = "interruptionPolicy", value = data.interruptionPolicy ?? "OnSuccess" });
            
            if (data.npcPrefab != null)
            {
                string npcPath = AssetDatabase.GetAssetPath(data.npcPrefab);
                string npcGuid = AssetDatabase.AssetPathToGUID(npcPath);
                metadata.Add(new MetadataEntry { key = "npcGuid", value = npcGuid });
            }
            
            if (data.task != null)
            {
                string taskPath = AssetDatabase.GetAssetPath(data.task);
                string taskGuid = AssetDatabase.AssetPathToGUID(taskPath);
                metadata.Add(new MetadataEntry { key = "taskGuid", value = taskGuid });
            }
            
            if (data.dialogue != null)
            {
                string dialoguePath = AssetDatabase.GetAssetPath(data.dialogue);
                string dialogueGuid = AssetDatabase.AssetPathToGUID(dialoguePath);
                metadata.Add(new MetadataEntry { key = "dialogueGuid", value = dialogueGuid });
            }
            
            if (data.conditionQuest != null)
            {
                string questPath = AssetDatabase.GetAssetPath(data.conditionQuest);
                string questGuid = AssetDatabase.AssetPathToGUID(questPath);
                metadata.Add(new MetadataEntry { key = "conditionQuestGuid", value = questGuid });
            }
            
            if (data.interactionQuest != null)
            {
                string interactionQuestPath = AssetDatabase.GetAssetPath(data.interactionQuest);
                string interactionQuestGuid = AssetDatabase.AssetPathToGUID(interactionQuestPath);
                metadata.Add(new MetadataEntry { key = "interactionQuestGuid", value = interactionQuestGuid });
            }
            
            // Save command-specific fields
            metadata.Add(new MetadataEntry { key = "commandFlagId", value = data.commandFlagId ?? "" });
            metadata.Add(new MetadataEntry { key = "commandFlagState", value = data.commandFlagState.ToString() });
            metadata.Add(new MetadataEntry { key = "commandQuantity", value = data.commandQuantity.ToString() });
            metadata.Add(new MetadataEntry { key = "commandIsAdd", value = data.commandIsAdd.ToString() });
            metadata.Add(new MetadataEntry { key = "commandMoneyAmount", value = data.commandMoneyAmount.ToString() });
            metadata.Add(new MetadataEntry { key = "commandExperience", value = data.commandExperience.ToString() });
            metadata.Add(new MetadataEntry { key = "commandWaitTime", value = data.commandWaitTime.ToString() });
            
            if (data.commandItem != null)
            {
                string itemPath = AssetDatabase.GetAssetPath(data.commandItem);
                string itemGuid = AssetDatabase.AssetPathToGUID(itemPath);
                metadata.Add(new MetadataEntry { key = "commandItemGuid", value = itemGuid });
            }
            
            if (data.commandTask != null)
            {
                string cmdTaskPath = AssetDatabase.GetAssetPath(data.commandTask);
                string cmdTaskGuid = AssetDatabase.AssetPathToGUID(cmdTaskPath);
                metadata.Add(new MetadataEntry { key = "commandTaskGuid", value = cmdTaskGuid });
            }
            
            if (data.commandQuest != null)
            {
                string cmdQuestPath = AssetDatabase.GetAssetPath(data.commandQuest);
                string cmdQuestGuid = AssetDatabase.AssetPathToGUID(cmdQuestPath);
                metadata.Add(new MetadataEntry { key = "commandQuestGuid", value = cmdQuestGuid });
            }
            
            if (data.commandDialogue != null)
            {
                string cmdDialoguePath = AssetDatabase.GetAssetPath(data.commandDialogue);
                string cmdDialogueGuid = AssetDatabase.AssetPathToGUID(cmdDialoguePath);
                metadata.Add(new MetadataEntry { key = "commandDialogueGuid", value = cmdDialogueGuid });
            }
            
            var nodeData = new NodeData
            {
                nodeId = nodeId,
                nodeType = "ConditionalNPCInteraction",
                dataReference = data.dialogue?.name ?? "",
                position = position,
                role = "",
                metadata = metadata
            };
            
            m_graphData.SetNode(nodeData);
            EditorUtility.SetDirty(m_graphData);
        }

        private class ConditionalNPCInteractionData
        {
            public GameObject npcPrefab;
            public Gyvr.Mythril2D.QuestTask task;
            public Gyvr.Mythril2D.DialogueSequence dialogue;
            public bool isApplied;
            public string interactionType = "DialogueInteraction";
            public string conditionType = "IsQuestTaskActive";
            public Gyvr.Mythril2D.Quest conditionQuest;
            public string conditionQuestState = "Active";
            public string conditionGameFlag = "";
            public Gyvr.Mythril2D.Quest interactionQuest;
            public string commandType = "SetGameFlag";
            public string interruptionPolicy = "OnSuccess";
            // Command-specific fields
            public string commandFlagId = "";
            public bool commandFlagState = true;
            public Gyvr.Mythril2D.Item commandItem;
            public int commandQuantity = 1;
            public bool commandIsAdd = true;
            public int commandMoneyAmount = 100;
            public int commandExperience = 10;
            public Gyvr.Mythril2D.QuestTask commandTask;
            public Gyvr.Mythril2D.Quest commandQuest;
            public Gyvr.Mythril2D.DialogueSequence commandDialogue;
            public float commandWaitTime = 1f;
        }

        private List<string> GetDialogueSequencesForDropdown()
        {
            var choices = new List<string> { "None" };
            
            if (m_quest == null) return choices;
            
            string questPath = AssetDatabase.GetAssetPath(m_quest);
            string questFolder = System.IO.Path.GetDirectoryName(questPath);
            
            string[] guids = AssetDatabase.FindAssets("t:DialogueSequence", new[] { questFolder });
            foreach (var guid in guids)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                var dialogue = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.DialogueSequence>(path);
                if (dialogue != null && !choices.Contains(dialogue.name))
                {
                    choices.Add(dialogue.name);
                }
            }
            
            return choices;
        }
        
        private Gyvr.Mythril2D.DialogueSequence FindDialogueSequenceByName(string name)
        {
            if (string.IsNullOrEmpty(name) || name == "None") return null;
            
            if (m_quest != null)
            {
                string questPath = AssetDatabase.GetAssetPath(m_quest);
                string questFolder = System.IO.Path.GetDirectoryName(questPath);
                
                string[] guids = AssetDatabase.FindAssets("t:DialogueSequence", new[] { questFolder });
                foreach (var guid in guids)
                {
                    string path = AssetDatabase.GUIDToAssetPath(guid);
                    var dialogue = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.DialogueSequence>(path);
                    if (dialogue != null && dialogue.name == name)
                    {
                        return dialogue;
                    }
                }
            }
            
            return null;
        }
        
        private List<string> GetQuestTasksForDropdown()
        {
            var choices = new List<string> { "None" };
            
            if (m_quest == null || m_quest.tasks == null) return choices;
            
            foreach (var task in m_quest.tasks)
            {
                if (task != null && !choices.Contains(task.name))
                {
                    choices.Add(task.name);
                }
            }
            
            return choices;
        }

        private void RefreshTaskDropdownChoices(PopupField<string> taskDropdown)
        {
            var newChoices = GetQuestTasksForDropdown();
            string currentValue = taskDropdown.value;
            
            var choicesField = typeof(PopupField<string>).GetProperty("choices", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            if (choicesField != null)
            {
                choicesField.SetValue(taskDropdown, newChoices);
            }
            
            if (newChoices.Contains(currentValue))
            {
                taskDropdown.SetValueWithoutNotify(currentValue);
            }
            else if (newChoices.Count > 0)
            {
                taskDropdown.SetValueWithoutNotify(newChoices[0]);
            }
        }
        
        private Gyvr.Mythril2D.QuestTask FindQuestTaskByName(string name)
        {
            if (string.IsNullOrEmpty(name) || name == "None") return null;
            
            if (m_quest != null && m_quest.tasks != null)
            {
                foreach (var task in m_quest.tasks)
                {
                    if (task != null && task.name == name)
                    {
                        return task;
                    }
                }
            }
            
            return null;
        }
        
        private void OnConditionTypeChanged(Node node)
        {
            if (!(node.userData is ConditionalNPCInteractionData data)) return;
            
            var conditionDropdown = node.Q<PopupField<string>>("condition-type-dropdown");
            string conditionType = conditionDropdown?.value ?? "IsQuestTaskActive";
            data.conditionType = conditionType;
            
            var taskLabel = node.Q<Label>("task-label");
            var taskDropdown = node.Q<PopupField<string>>("task-dropdown");
            var questLabel = node.Q<Label>("quest-label");
            var questField = node.Q<UnityEditor.UIElements.ObjectField>("quest-field");
            var questStateLabel = node.Q<Label>("quest-state-label");
            var questStateDropdown = node.Q<PopupField<string>>("quest-state-dropdown");
            var gameFlagLabel = node.Q<Label>("game-flag-label");
            var gameFlagField = node.Q<TextField>("game-flag-field");
            
            bool showTask = (conditionType == "IsQuestTaskActive" || conditionType == "IsQuestTaskInState");
            bool showQuest = (conditionType == "IsQuestInState");
            bool showState = (conditionType == "IsQuestInState" || conditionType == "IsQuestTaskInState");
            bool showGameFlag = (conditionType == "IsGameFlagSet");
            
            if (taskLabel != null) taskLabel.style.display = showTask ? DisplayStyle.Flex : DisplayStyle.None;
            if (taskDropdown != null) taskDropdown.style.display = showTask ? DisplayStyle.Flex : DisplayStyle.None;
            if (questLabel != null) questLabel.style.display = showQuest ? DisplayStyle.Flex : DisplayStyle.None;
            if (questField != null) questField.style.display = showQuest ? DisplayStyle.Flex : DisplayStyle.None;
            if (questStateLabel != null) questStateLabel.style.display = showState ? DisplayStyle.Flex : DisplayStyle.None;
            if (questStateDropdown != null) questStateDropdown.style.display = showState ? DisplayStyle.Flex : DisplayStyle.None;
            if (gameFlagLabel != null) gameFlagLabel.style.display = showGameFlag ? DisplayStyle.Flex : DisplayStyle.None;
            if (gameFlagField != null) gameFlagField.style.display = showGameFlag ? DisplayStyle.Flex : DisplayStyle.None;
            
            OnConditionalNPCInteractionChanged(node);
        }
        
        private void UpdateWireHintLabel(Node node)
        {
            if (!(node.userData is ConditionalNPCInteractionData data)) return;
            
            var wireHintLabel = node.Q<Label>("wire-hint-label");
            if (wireHintLabel == null) return;
            
            var outputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
            bool isDialogueInteraction = (data.interactionType == "DialogueInteraction");
            bool isOutputConnected = (outputPort != null && outputPort.connected);
            
            if (isDialogueInteraction && !isOutputConnected)
            {
                wireHintLabel.style.display = DisplayStyle.Flex;
            }
            else
            {
                wireHintLabel.style.display = DisplayStyle.None;
            }
        }
        
        private void UpdateNPCInteractionStatusLabel(Node node, ConditionalNPCInteractionData data)
        {
            var nameLabel = node.Q<Label>("npc-name-label");
            if (nameLabel == null) return;
            
            bool isDialogueInteraction = (data.interactionType == "DialogueInteraction");
            
            if (data.npcPrefab == null)
            {
                nameLabel.text = "(select NPC)";
                nameLabel.style.color = new Color(1f, 0.6f, 0.6f);
            }
            else if (data.task == null)
            {
                nameLabel.text = $"{data.npcPrefab.name} (connect task ←)";
                nameLabel.style.color = new Color(1f, 0.8f, 0.5f);
            }
            else if (isDialogueInteraction && data.dialogue == null)
            {
                nameLabel.text = $"{data.npcPrefab.name} (connect dialogue →)";
                nameLabel.style.color = new Color(1f, 0.8f, 0.5f);
            }
            else if (data.isApplied)
            {
                if (isDialogueInteraction && data.dialogue != null)
                    nameLabel.text = $"✓ {data.npcPrefab.name} → {data.dialogue.name}";
                else
                    nameLabel.text = $"✓ {data.npcPrefab.name} ({data.interactionType})";
                nameLabel.style.color = new Color(0.5f, 1f, 0.5f);
            }
            else
            {
                nameLabel.text = $"→ {data.npcPrefab.name}";
                nameLabel.style.color = new Color(1f, 1f, 1f, 0.5f);
            }
        }

        private void UpdateConditionalNPCInteractionFromConnections(Node node)
        {
            if (!(node.userData is ConditionalNPCInteractionData data)) return;

            var inputPort = node.Query<Port>().Where(p => p.direction == Direction.Input).First();
            if (inputPort != null && inputPort.connected)
            {
                var edge = inputPort.connections.FirstOrDefault();
                if (edge?.output?.node?.userData is System.Tuple<Gyvr.Mythril2D.QuestTask, int> taskTuple)
                {
                    data.task = taskTuple.Item1;
                }
            }
            else
            {
                data.task = null;
            }

            var outputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
            if (outputPort != null && outputPort.connected)
            {
                var edge = outputPort.connections.FirstOrDefault();
                if (edge?.input?.node?.userData is Gyvr.Mythril2D.DialogueSequence dialogue)
                {
                    data.dialogue = dialogue;
                }
            }
            else
            {
                data.dialogue = null;
            }

            SaveConditionalNPCInteractionNodeData(node.name, data, node.GetPosition().position);
        }

        public void LoadGraph(QuestGraphData graphData)
        {
            if (graphData == null || graphData.questAsset == null)
            {
                return;
            }

            m_graphData = graphData;
            m_quest = graphData.questAsset;
            
            m_graphData.LoadDialogueRolesFromQuest(m_quest);

            ClearGraph();

            bool hasSavedLayout = m_graphData.nodes.Count > 0;

            if (hasSavedLayout)
            {
                LoadNodesFromData();
                EnsureNPCConditionalInteractionDialoguesExist();
                GenerateConnectionsFromQuestData();
                DiscoverAndCreateNPCConditionalInteractionNodes();
                WireConditionalNPCInteractionDialogues();
            }
            else
            {
                GenerateInitialNodes();
                EnsureNPCConditionalInteractionDialoguesExist();
                DiscoverAndCreateNPCConditionalInteractionNodes();
                WireConditionalNPCInteractionDialogues();
            }

            UpdateViewTransform(m_graphData.viewPosition, m_graphData.viewScale);
            
        }

        private new void UpdateViewTransform(Vector3 position, Vector3 scale)
        {
            base.UpdateViewTransform(position, scale);
        }

        private void ClearGraph()
        {
            var stickyNotes = this.Query<StickyNote>().ToList();
            foreach (var note in stickyNotes)
            {
                RemoveElement(note);
            }
            
            edges.ToList().ForEach(edge => RemoveElement(edge));
            
            graphElements.ForEach(element =>
            {
                if (element is Node node)
                {
                    RemoveElement(node);
                }
            });

            m_nodeViews.Clear();
        }

        private void GenerateInitialNodes()
        {
            Vector2 position = new Vector2(100, 100);
            Port lastOutputPort = null;
            Port mainFlowOutputPort = null;

            var questInfoNode = CreateQuestInfoNode(position);
            lastOutputPort = questInfoNode.Q<VisualElement>("custom-port-container")?.Q<Port>();
            mainFlowOutputPort = lastOutputPort;
            if (mainFlowOutputPort == null)
            {
            }
            position.x += 500;

            if (m_quest.questOfferDialogue != null)
            {
                var offerNode = CreateDialogueNode(m_quest.questOfferDialogue, "Quest Offer", position, role: "QuestOffer");
                if (offerNode != null && mainFlowOutputPort != null)
                {
                    var offerInputPort = FindPort(offerNode, Direction.Input, "");
                    if (offerInputPort != null)
                    {
                        CreateAndAddEdge(mainFlowOutputPort, offerInputPort);
                    }
                }
                
                CreateDialogueTreeBranches(m_quest.questOfferDialogue, new Vector2(position.x, position.y + 300));
                
                if (offerNode != null)
                {
                    mainFlowOutputPort = offerNode.outputContainer.Q<Port>();
                    if (mainFlowOutputPort == null)
                    {
                    }
                }
                
                position.x += 500;

                if (m_quest.questHintDialogue != null && offerNode != null)
                {
                    Vector2 hintPos = new Vector2(position.x - 500, position.y + 300);
                    var questHintNode = CreateDialogueNode(m_quest.questHintDialogue, "Quest Hint (Default)", hintPos, role: "QuestHint");
                    
                    if (questHintNode == null)
                    {
                    }
                    else
                    {
                    }

                    CreateDialogueTreeBranches(m_quest.questHintDialogue, new Vector2(hintPos.x, hintPos.y + 300));
                }
                else
                {
                    if (m_quest.questHintDialogue != null)
                    {
                    }
                    else
                    {
                    }
                }
            }

            Port lastTaskOutputPort = null;
            if (m_quest.tasks != null && m_quest.tasks.Length > 0)
            {
                
                for (int i = 0; i < m_quest.tasks.Length; i++)
                {
                    var taskNode = CreateTaskNode(i, position);
                    
                    if (taskNode != null)
                    {
                        var inputPort = taskNode.Query<Port>().Where(p => p.direction == Direction.Input).ToList().FirstOrDefault();
                        if (mainFlowOutputPort != null && inputPort != null)
                        {
                            CreateAndAddEdge(mainFlowOutputPort, inputPort);
                        }
                        
                        if (m_quest.questHintDialogueOverrides != null && 
                            m_quest.questHintDialogueOverrides.ContainsKey(m_quest.tasks[i]))
                        {
                            var overrideDialogue = m_quest.questHintDialogueOverrides[m_quest.tasks[i]];
                            if (overrideDialogue != null)
                            {
                                Vector2 overridePos = new Vector2(position.x - 500, position.y);
                                var overrideNode = CreateDialogueNode(overrideDialogue, "Hint Override", overridePos, role: $"HintOverride_{i}");
                                
                                if (overrideNode != null)
                                {
                                    var hintPort = taskNode.Query<Port>().Where(p => p.direction == Direction.Output && p.portName == "Hint Override").First();
                                    var overrideInputPort = FindPort(overrideNode, Direction.Input, "");
                                    if (hintPort != null && overrideInputPort != null)
                                    {
                                        CreateAndAddEdge(hintPort, overrideInputPort);
                                    }
                                }
                                
                                CreateDialogueTreeBranches(overrideDialogue, new Vector2(overridePos.x, overridePos.y + 300));
                            }
                        }
                        
                        var taskPorts = taskNode.Query<Port>().Where(p => 
                            p.direction == Direction.Output && 
                            p.portName != "Hint Override" &&
                            !p.portName.Contains("Hint")).ToList();
                        
                        mainFlowOutputPort = taskPorts.FirstOrDefault(p => string.IsNullOrEmpty(p.portName)) ?? taskPorts.FirstOrDefault();
                        lastTaskOutputPort = mainFlowOutputPort;
                        if (mainFlowOutputPort == null)
                        {
                        }
                    }
                    
                    position.x += 500;
                }
            }

            if (m_quest.questCompletedDialogue != null)
            {
                Vector2 completedPos = position;
                var completedNode = CreateDialogueNode(m_quest.questCompletedDialogue, "Quest Completed", completedPos, role: "QuestCompleted");
                
                if (completedNode == null)
                {
                }
                else
                {
                }

                CreateDialogueTreeBranches(m_quest.questCompletedDialogue, new Vector2(completedPos.x, completedPos.y + 300));
            }
            else
            {
            }
            
        }

        private void RestoreNodePositions()
        {
            int restored = 0;
            foreach (var nodeData in m_graphData.nodes)
            {
                if (m_nodeViews.TryGetValue(nodeData.nodeId, out Node node))
                {
                    node.SetPosition(new Rect(nodeData.position, Vector2.zero));
                    restored++;
                }
                else
                {
                }
            }
        }

        private void LoadNodesFromData()
        {
            
            var nodesToLoad = new List<NodeData>(m_graphData.nodes);
            
            foreach (var nodeData in nodesToLoad)
            {
                
                Node node = CreateNodeFromData(nodeData);
                if (node != null)
                {
                    AddElement(node);
                    m_nodeViews[nodeData.nodeId] = node;
                }
                else
                {
                }
            }
            
            EnsureEssentialNodesExist();
            
            EnsureAllDialogueBranchesExist();

            if (m_quest.tasks != null && m_quest.tasks.Length > 0)
            {
                int existingTaskNodes = m_graphData.nodes.Count(n => n.nodeType == "Task");
                int actualTasks = m_quest.tasks.Length;

                if (actualTasks > existingTaskNodes)
                {
                    
                    float maxY = 200;
                    foreach (var nodeData in m_graphData.nodes)
                    {
                        if (nodeData.position.y > maxY)
                        {
                            maxY = nodeData.position.y;
                        }
                    }
                    maxY += 150;

                    for (int i = existingTaskNodes; i < actualTasks; i++)
                    {
                        Vector2 position = new Vector2(100, maxY);
                        CreateTaskNode(i, position);
                        maxY += 150;
                    }
                }
            }

            var stickyNotes = this.Query<StickyNote>().ToList();
            foreach (var note in stickyNotes)
            {
                note.RemoveFromHierarchy();
            }

            RestoreConnections();
            
            RestoreStickyNotes();
            
        }
        
        private void EnsureEssentialNodesExist()
        {
            float rightMostX = 100;
            float topY = 100;
            
            foreach (var kvp in m_nodeViews)
            {
                var pos = kvp.Value.GetPosition().position;
                if (pos.x > rightMostX) rightMostX = pos.x;
                if (pos.y < topY) topY = pos.y;
            }
            
            if (!m_nodeViews.ContainsKey("quest-info"))
            {
                var questInfoNode = CreateQuestInfoNode(new Vector2(100, topY));
                if (questInfoNode != null)
                {
                    AddElement(questInfoNode);
                    m_nodeViews["quest-info"] = questInfoNode;
                }
            }
            
            bool hasOfferNode = m_nodeViews.Values.Any(n => n.userData is UnityEngine.Object obj && obj == m_quest.questOfferDialogue);
            if (m_quest.questOfferDialogue != null && !hasOfferNode)
            {
                var offerNode = CreateDialogueNode(m_quest.questOfferDialogue, "Quest Offer", new Vector2(rightMostX + 300, topY), role: "QuestOffer");
                if (offerNode != null)
                {
                    AddElement(offerNode);
                    m_nodeViews[offerNode.name] = offerNode;
                    
                    CreateDialogueTreeBranches(m_quest.questOfferDialogue, new Vector2(offerNode.GetPosition().position.x, offerNode.GetPosition().position.y + 300));
                }
            }
            
            bool hasHintNode = m_nodeViews.Values.Any(n => n.userData is UnityEngine.Object obj && obj == m_quest.questHintDialogue);
            if (m_quest.questHintDialogue != null && !hasHintNode)
            {
                var hintNode = CreateDialogueNode(m_quest.questHintDialogue, "Quest Hint (Default)", new Vector2(rightMostX + 300, topY + 400), role: "QuestHint");
                if (hintNode != null)
                {
                    AddElement(hintNode);
                    m_nodeViews[hintNode.name] = hintNode;
                    
                    CreateDialogueTreeBranches(m_quest.questHintDialogue, new Vector2(hintNode.GetPosition().position.x, hintNode.GetPosition().position.y + 300));
                }
            }
            
            bool hasCompletedNode = m_nodeViews.Values.Any(n => n.userData is UnityEngine.Object obj && obj == m_quest.questCompletedDialogue);
            if (m_quest.questCompletedDialogue != null && !hasCompletedNode)
            {
                var completedNode = CreateDialogueNode(m_quest.questCompletedDialogue, "Quest Completed", new Vector2(rightMostX + 600, topY), role: "QuestCompleted");
                if (completedNode != null)
                {
                    AddElement(completedNode);
                    m_nodeViews[completedNode.name] = completedNode;
                    
                    CreateDialogueTreeBranches(m_quest.questCompletedDialogue, new Vector2(completedNode.GetPosition().position.x, completedNode.GetPosition().position.y + 300));
                }
            }
        }
        
        private void EnsureNPCConditionalInteractionDialoguesExist()
        {
            var allDialogues = new HashSet<DialogueSequence>();
            var dialoguesToProcess = new Queue<DialogueSequence>();
            
            FindDialoguesFromNPCInteractions(allDialogues, dialoguesToProcess);
            
            var rootDialogues = new HashSet<DialogueSequence>(allDialogues);
            var branchLabels = new Dictionary<DialogueSequence, string>();
            
            while (dialoguesToProcess.Count > 0)
            {
                var dialogue = dialoguesToProcess.Dequeue();
                if (dialogue.options == null) continue;
                
                foreach (var option in dialogue.options)
                {
                    if (option.sequence != null && !allDialogues.Contains(option.sequence))
                    {
                        allDialogues.Add(option.sequence);
                        dialoguesToProcess.Enqueue(option.sequence);
                        
                        if (!string.IsNullOrEmpty(option.name))
                        {
                            branchLabels[option.sequence] = option.name;
                        }
                    }
                }
            }
            
            int nodesCreated = 0;
            foreach (var dialogue in allDialogues)
            {
                string nodeId = $"dialogue-{dialogue.GetInstanceID()}";
                
                bool nodeExists = m_nodeViews.ContainsKey(nodeId) || 
                                  m_nodeViews.Values.Any(n => n.userData is UnityEngine.Object obj && obj == dialogue);
                
                if (!nodeExists)
                {
                    Vector2 position = new Vector2(500 + (nodesCreated * 400), 800);
                    
                    string label;
                    if (rootDialogues.Contains(dialogue))
                        label = "Conditional Dialogue";
                    else if (branchLabels.TryGetValue(dialogue, out string optionLabel))
                        label = optionLabel;
                    else
                        label = dialogue.name;
                    
                    var dialogueNode = CreateDialogueNode(dialogue, label, position);
                    if (dialogueNode != null)
                    {
                        AddElement(dialogueNode);
                        m_nodeViews[dialogueNode.name] = dialogueNode;
                        nodesCreated++;
                    }
                }
            }
        }
        
        private void EnsureAllDialogueBranchesExist()
        {
            var allDialogues = new HashSet<DialogueSequence>();
            var dialoguesToProcess = new Queue<DialogueSequence>();
            
            if (m_quest.questOfferDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questOfferDialogue);
                allDialogues.Add(m_quest.questOfferDialogue);
            }
            if (m_quest.questHintDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questHintDialogue);
                allDialogues.Add(m_quest.questHintDialogue);
            }
            if (m_quest.questCompletedDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questCompletedDialogue);
                allDialogues.Add(m_quest.questCompletedDialogue);
            }
            
            if (m_quest.questHintDialogueOverrides != null)
            {
                foreach (var kvp in m_quest.questHintDialogueOverrides)
                {
                    if (kvp.Value != null)
                    {
                        dialoguesToProcess.Enqueue(kvp.Value);
                        allDialogues.Add(kvp.Value);
                    }
                }
            }
            
            if (m_quest.tasks != null)
            {
                foreach (var task in m_quest.tasks)
                {
                    if (task is TalkToNPCTask talkToTask && talkToTask.dialogue != null)
                    {
                        if (!allDialogues.Contains(talkToTask.dialogue))
                        {
                            dialoguesToProcess.Enqueue(talkToTask.dialogue);
                            allDialogues.Add(talkToTask.dialogue);
                        }
                    }
                }
            }
            
            FindDialoguesFromQuestReferences(allDialogues, dialoguesToProcess);
            
            while (dialoguesToProcess.Count > 0)
            {
                var dialogue = dialoguesToProcess.Dequeue();
                if (dialogue.options == null) continue;
                
                foreach (var option in dialogue.options)
                {
                    if (option.sequence != null && !allDialogues.Contains(option.sequence))
                    {
                        allDialogues.Add(option.sequence);
                        dialoguesToProcess.Enqueue(option.sequence);
                    }
                }
            }
            
            foreach (var dialogue in allDialogues)
            {
                string expectedNodeId = $"dialogue-{dialogue.GetInstanceID()}";
                
                bool nodeExists = m_nodeViews.ContainsKey(expectedNodeId) || 
                                  m_nodeViews.Values.Any(n => n.userData is UnityEngine.Object obj && obj == dialogue);
                
                if (!nodeExists)
                {
                    Vector2 position = FindPositionForMissingDialogue(dialogue, allDialogues);
                    
                    string label = GetDialogueLabelFromParent(dialogue, allDialogues);
                    
                    var dialogueNode = CreateDialogueNode(dialogue, label, position);
                    if (dialogueNode != null)
                    {
                        AddElement(dialogueNode);
                        m_nodeViews[dialogueNode.name] = dialogueNode;
                    }
                }
            }
        }
        
        private Vector2 FindPositionForMissingDialogue(DialogueSequence dialogue, HashSet<DialogueSequence> allDialogues)
        {
            if (m_quest.tasks != null)
            {
                for (int i = 0; i < m_quest.tasks.Length; i++)
                {
                    if (m_quest.tasks[i] is TalkToNPCTask talkToTask && talkToTask.dialogue == dialogue)
                    {
                        string taskNodeId = $"task-{i}";
                        if (m_nodeViews.TryGetValue(taskNodeId, out Node taskNode))
                        {
                            var taskPos = taskNode.GetPosition().position;
                            return new Vector2(taskPos.x + 350, taskPos.y);
                        }
                    }
                }
            }
            
            foreach (var parentDialogue in allDialogues)
            {
                if (parentDialogue.options == null) continue;
                
                for (int i = 0; i < parentDialogue.options.Length; i++)
                {
                    if (parentDialogue.options[i].sequence == dialogue)
                    {
                        string parentNodeId = $"dialogue-{parentDialogue.GetInstanceID()}";
                        if (m_nodeViews.TryGetValue(parentNodeId, out Node parentNode))
                        {
                            var parentPos = parentNode.GetPosition().position;
                            return new Vector2(parentPos.x + 300, parentPos.y + (i * 180));
                        }
                        
                        var parentByData = m_nodeViews.Values.FirstOrDefault(n => n.userData is UnityEngine.Object obj && obj == parentDialogue);
                        if (parentByData != null)
                        {
                            var parentPos = parentByData.GetPosition().position;
                            return new Vector2(parentPos.x + 300, parentPos.y + (i * 180));
                        }
                    }
                }
            }
            
            float maxX = 100;
            float maxY = 100;
            foreach (var node in m_nodeViews.Values)
            {
                var pos = node.GetPosition().position;
                if (pos.x > maxX) maxX = pos.x;
                if (pos.y > maxY) maxY = pos.y;
            }
            return new Vector2(maxX + 100, maxY + 100);
        }
        
        private HashSet<DialogueSequence> CollectAllDialogues()
        {
            var allDialogues = new HashSet<DialogueSequence>();
            var dialoguesToProcess = new Queue<DialogueSequence>();
            
            if (m_quest.questOfferDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questOfferDialogue);
                allDialogues.Add(m_quest.questOfferDialogue);
            }
            if (m_quest.questHintDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questHintDialogue);
                allDialogues.Add(m_quest.questHintDialogue);
            }
            if (m_quest.questCompletedDialogue != null)
            {
                dialoguesToProcess.Enqueue(m_quest.questCompletedDialogue);
                allDialogues.Add(m_quest.questCompletedDialogue);
            }
            
            if (m_quest.questHintDialogueOverrides != null)
            {
                foreach (var kvp in m_quest.questHintDialogueOverrides)
                {
                    if (kvp.Value != null)
                    {
                        dialoguesToProcess.Enqueue(kvp.Value);
                        allDialogues.Add(kvp.Value);
                    }
                }
            }
            
            if (m_quest.tasks != null)
            {
                foreach (var task in m_quest.tasks)
                {
                    if (task is TalkToNPCTask talkToTask && talkToTask.dialogue != null)
                    {
                        if (!allDialogues.Contains(talkToTask.dialogue))
                        {
                            dialoguesToProcess.Enqueue(talkToTask.dialogue);
                            allDialogues.Add(talkToTask.dialogue);
                        }
                    }
                }
            }
            
            while (dialoguesToProcess.Count > 0)
            {
                var dialogue = dialoguesToProcess.Dequeue();
                if (dialogue.options == null) continue;
                
                foreach (var option in dialogue.options)
                {
                    if (option.sequence != null && !allDialogues.Contains(option.sequence))
                    {
                        allDialogues.Add(option.sequence);
                        dialoguesToProcess.Enqueue(option.sequence);
                    }
                }
            }
            
            return allDialogues;
        }
        
        private string GetDialogueLabelFromParent(DialogueSequence dialogue, HashSet<DialogueSequence> allDialogues)
        {
            if (m_quest.tasks != null)
            {
                for (int i = 0; i < m_quest.tasks.Length; i++)
                {
                    if (m_quest.tasks[i] is TalkToNPCTask talkToTask && talkToTask.dialogue == dialogue)
                    {
                        string taskTitle = GetTaskNodeTitle(m_quest.tasks[i], i);
                        return $"Task Dialogue";
                    }
                }
            }
            
            foreach (var parentDialogue in allDialogues)
            {
                if (parentDialogue.options == null) continue;
                
                foreach (var option in parentDialogue.options)
                {
                    if (option.sequence == dialogue)
                    {
                        if (!string.IsNullOrEmpty(option.name))
                            return option.name;
                        
                        switch (option.message.type)
                        {
                            case Gyvr.Mythril2D.EDialogueMessageType.Accept:
                                return "Accept";
                            case Gyvr.Mythril2D.EDialogueMessageType.Decline:
                                return "Decline";
                            case Gyvr.Mythril2D.EDialogueMessageType.Custom:
                                return option.message.customMessage ?? "Option";
                            default:
                                return "Branch";
                        }
                    }
                }
            }
            
            return dialogue.name;
        }

        private void RestoreConnections()
        {
            if (m_graphData == null || m_graphData.connections == null)
            {
                return;
            }

            if (m_graphData.connections.Count == 0)
            {
                return;
            }

            foreach (var connectionData in m_graphData.connections)
            {
                if (connectionData.fromPortName == "Hint Override" && 
                    m_nodeViews.TryGetValue(connectionData.toNodeId, out Node toNodeCheck) &&
                    toNodeCheck.userData is UnityEngine.Object toObj && toObj == m_quest.questCompletedDialogue)
                {
                    continue;
                }
                
                if (!m_nodeViews.TryGetValue(connectionData.fromNodeId, out Node fromNode))
                {
                    continue;
                }
                
                if (!m_nodeViews.TryGetValue(connectionData.toNodeId, out Node toNode))
                {
                    continue;
                }

                Port outputPort = FindPort(fromNode, Direction.Output, connectionData.fromPortName);
                Port inputPort = FindPort(toNode, Direction.Input, connectionData.toPortName);

                if (outputPort == null)
                {
                    var allOutputPorts = fromNode.Query<Port>().Where(p => p.direction == Direction.Output).ToList();
                }
                
                if (inputPort == null)
                {
                    var allInputPorts = toNode.Query<Port>().Where(p => p.direction == Direction.Input).ToList();
                }
                
                if (outputPort != null && inputPort != null)
                {
                    var edge = outputPort.ConnectTo(inputPort);
                    
                    if (connectionData.toPortName == "dialogue" && connectionData.toNodeId.StartsWith("task-"))
                    {
                        if (int.TryParse(connectionData.toNodeId.Replace("task-", ""), out int taskIndex))
                        {
                            if (taskIndex < m_quest.tasks.Length)
                            {
                                var task = m_quest.tasks[taskIndex];
                                if (task is TalkToNPCTask talkToTask)
                                {
                                    if (fromNode.userData is DialogueSequence dialogueData)
                                    {
                                        if (talkToTask.dialogue != dialogueData)
                                        {
                                            var serializedTask = new SerializedObject(talkToTask);
                                            serializedTask.FindProperty("dialogue").objectReferenceValue = dialogueData;
                                            serializedTask.ApplyModifiedProperties();
                                            AssetDatabase.SaveAssets();
                                        }
                                    }
                                }
                            }
                        }
                        
                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                    }
                    else
                    {
                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                    }
                    
                    AddElement(edge);
                }
            }
        }
        
        private void GenerateConnectionsFromQuestData()
        {
            var dialogueNodes = new Dictionary<DialogueSequence, Node>();
            foreach (var kvp in m_nodeViews)
            {
                if (kvp.Value.userData is DialogueSequence dialogue && dialogue != null)
                {
                    dialogueNodes[dialogue] = kvp.Value;
                }
            }
            
            if (m_quest.questOfferDialogue != null && m_nodeViews.ContainsKey("quest-info") && dialogueNodes.ContainsKey(m_quest.questOfferDialogue))
            {
                var questInfoNode = m_nodeViews["quest-info"];
                var offerNode = dialogueNodes[m_quest.questOfferDialogue];
                
                var outputPort = questInfoNode.Q<VisualElement>("custom-port-container")?.Q<Port>();
                var inputPort = FindPort(offerNode, Direction.Input, "");
                
                if (outputPort != null && inputPort != null && !IsConnected(outputPort, inputPort))
                {
                    var edge = outputPort.ConnectTo(inputPort);
                    edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                    edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                    AddElement(edge);
                    
                    var connectionData = new ConnectionData
                    {
                        fromNodeId = questInfoNode.name,
                        toNodeId = offerNode.name,
                        fromPortName = outputPort.portName ?? "",
                        toPortName = inputPort.portName ?? ""
                    };
                    m_graphData.AddConnection(connectionData);
                }
            }
            
            foreach (var kvp in dialogueNodes)
            {
                var dialogue = kvp.Key;
                var sourceNode = kvp.Value;
                
                if (dialogue.options == null)
                    continue;
                
                for (int i = 0; i < dialogue.options.Length; i++)
                {
                    var option = dialogue.options[i];
                    if (option.sequence != null && dialogueNodes.TryGetValue(option.sequence, out Node targetNode))
                    {
                        string portName = $"option-{i}";
                        var outputPort = FindPort(sourceNode, Direction.Output, portName);
                        var inputPort = FindPort(targetNode, Direction.Input, "");
                        
                        if (outputPort != null && inputPort != null && !IsConnected(outputPort, inputPort))
                        {
                            var edge = outputPort.ConnectTo(inputPort);
                            edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                            edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                            AddElement(edge);
                            
                            var connectionData = new ConnectionData
                            {
                                fromNodeId = sourceNode.name,
                                toNodeId = targetNode.name,
                                fromPortName = portName,
                                toPortName = ""
                            };
                            m_graphData.AddConnection(connectionData);
                        }
                    }
                }
            }
            
            if (m_quest.tasks != null && m_quest.tasks.Length > 0 && m_nodeViews.TryGetValue("task-0", out Node firstTaskNode))
            {
                var firstTaskInputPort = firstTaskNode.Query<Port>().Where(p => p.direction == Direction.Input).First();
                
                if (firstTaskInputPort != null)
                {
                    var acceptChainDialogues = new HashSet<DialogueSequence>();
                    CollectAcceptChainDialogues(m_quest.questOfferDialogue, acceptChainDialogues, new HashSet<int>());
                    
                    foreach (var kvp in dialogueNodes)
                    {
                        var dialogue = kvp.Key;
                        var dialogueNode = kvp.Value;
                        
                        if (dialogue == m_quest.questHintDialogue || dialogue == m_quest.questCompletedDialogue)
                            continue;
                        
                        if (dialogue.options != null)
                        {
                            for (int i = 0; i < dialogue.options.Length; i++)
                            {
                                var option = dialogue.options[i];
                                
                                if (option.message.type == Gyvr.Mythril2D.EDialogueMessageType.Accept && option.sequence == null)
                                {
                                    string portName = $"option-{i}";
                                    var outputPort = FindPort(dialogueNode, Direction.Output, portName);
                                    
                                    if (outputPort != null && !IsConnected(outputPort, firstTaskInputPort))
                                    {
                                        var edge = outputPort.ConnectTo(firstTaskInputPort);
                                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                        AddElement(edge);
                                        
                                        var connectionData = new ConnectionData
                                        {
                                            fromNodeId = dialogueNode.name,
                                            toNodeId = firstTaskNode.name,
                                            fromPortName = portName,
                                            toPortName = ""
                                        };
                                        m_graphData.AddConnection(connectionData);
                                    }
                                }
                            }
                        }
                        
                        if (dialogue.options == null || dialogue.options.Length == 0)
                        {
                            if (dialogue == m_quest.questOfferDialogue)
                                continue;
                            
                            bool isInAcceptChain = acceptChainDialogues.Contains(dialogue);
                            
                            if (isInAcceptChain)
                            {
                                var outputPort = FindPort(dialogueNode, Direction.Output, "");
                                
                                if (outputPort != null && !IsConnected(outputPort, firstTaskInputPort))
                                {
                                    var edge = outputPort.ConnectTo(firstTaskInputPort);
                                    edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                    edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                    AddElement(edge);
                                    
                                    var connectionData = new ConnectionData
                                    {
                                        fromNodeId = dialogueNode.name,
                                        toNodeId = firstTaskNode.name,
                                        fromPortName = "",
                                        toPortName = ""
                                    };
                                    m_graphData.AddConnection(connectionData);
                                }
                            }
                        }
                    }
                }
            }
            
            if (m_quest.questCompletedDialogue != null && dialogueNodes.ContainsKey(m_quest.questCompletedDialogue))
            {
                var completedNode = dialogueNodes[m_quest.questCompletedDialogue];
                
                Node lastTaskNode = null;
                if (m_quest.tasks != null && m_quest.tasks.Length > 0)
                {
                    string lastTaskNodeId = $"task-{m_quest.tasks.Length - 1}";
                    if (m_nodeViews.TryGetValue(lastTaskNodeId, out Node taskNode))
                    {
                        lastTaskNode = taskNode;
                    }
                }
                
                if (lastTaskNode != null)
                {
                    var outputPorts = lastTaskNode.Query<Port>().Where(p => 
                        p.direction == Direction.Output && 
                        p.portName != "Hint Override" && 
                        !p.portName.Contains("Hint") &&
                        p.portName != "dialogue").ToList();
                    
                    var outputPort = outputPorts.FirstOrDefault(p => string.IsNullOrEmpty(p.portName)) ?? outputPorts.FirstOrDefault();
                    var inputPort = FindPort(completedNode, Direction.Input, "");
                    
                    if (outputPort != null && inputPort != null && !IsConnected(outputPort, inputPort))
                    {
                        var edge = outputPort.ConnectTo(inputPort);
                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                        AddElement(edge);
                        
                        var connectionData = new ConnectionData
                        {
                            fromNodeId = lastTaskNode.name,
                            toNodeId = completedNode.name,
                            fromPortName = outputPort.portName ?? "",
                            toPortName = ""
                        };
                        m_graphData.AddConnection(connectionData);
                    }
                }
            }
            
            if (m_quest.questHintDialogueOverrides != null)
            {
                for (int i = 0; i < m_quest.tasks.Length; i++)
                {
                    var task = m_quest.tasks[i];
                    if (m_quest.questHintDialogueOverrides.TryGetValue(task, out DialogueSequence overrideDialogue) && overrideDialogue != null)
                    {
                        string taskNodeId = $"task-{i}";
                        if (m_nodeViews.TryGetValue(taskNodeId, out Node taskNode) && dialogueNodes.TryGetValue(overrideDialogue, out Node overrideNode))
                        {
                            var hintPort = taskNode.Query<Port>().Where(p => p.direction == Direction.Output && p.portName == "Hint Override").First();
                            var inputPort = FindPort(overrideNode, Direction.Input, "");
                            
                            if (hintPort != null && inputPort != null && !IsConnected(hintPort, inputPort))
                            {
                                var edge = hintPort.ConnectTo(inputPort);
                                edge.edgeControl.inputColor = new Color(0.9f, 0.6f, 0.2f);
                                edge.edgeControl.outputColor = new Color(0.9f, 0.6f, 0.2f);
                                AddElement(edge);
                                
                                var connectionData = new ConnectionData
                                {
                                    fromNodeId = taskNode.name,
                                    toNodeId = overrideNode.name,
                                    fromPortName = "Hint Override",
                                    toPortName = ""
                                };
                                m_graphData.AddConnection(connectionData);
                            }
                        }
                    }
                }
            }
            
            if (m_quest.tasks != null)
            {
                for (int i = 0; i < m_quest.tasks.Length; i++)
                {
                    if (m_quest.tasks[i] is TalkToNPCTask talkToTask && talkToTask.dialogue != null)
                    {
                        string taskNodeId = $"task-{i}";
                        if (m_nodeViews.TryGetValue(taskNodeId, out Node taskNode) && dialogueNodes.TryGetValue(talkToTask.dialogue, out Node dialogueNode))
                        {
                            var dialoguePort = taskNode.Query<Port>().Where(p => p.direction == Direction.Output && p.portName == "dialogue").First();
                            var inputPort = FindPort(dialogueNode, Direction.Input, "");
                            
                            if (dialoguePort != null && inputPort != null && !IsConnected(dialoguePort, inputPort))
                            {
                                var edge = dialoguePort.ConnectTo(inputPort);
                                edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                AddElement(edge);
                                
                                var connectionData = new ConnectionData
                                {
                                    fromNodeId = taskNode.name,
                                    toNodeId = dialogueNode.name,
                                    fromPortName = "dialogue",
                                    toPortName = ""
                                };
                                m_graphData.AddConnection(connectionData);
                            }
                        }
                    }
                }
            }
            
            EditorUtility.SetDirty(m_graphData);
        }
        
        private void CollectAcceptChainDialogues(DialogueSequence dialogue, HashSet<DialogueSequence> acceptChain, HashSet<int> visited)
        {
            if (dialogue == null) return;
            
            int dialogueId = dialogue.GetInstanceID();
            if (visited.Contains(dialogueId)) return;
            visited.Add(dialogueId);
            
            if (dialogue.options == null) return;
            
            foreach (var option in dialogue.options)
            {
                if (option.sequence == null) continue;
                
                if (option.message.type == Gyvr.Mythril2D.EDialogueMessageType.Accept)
                {
                    acceptChain.Add(option.sequence);
                    CollectAcceptChainDialoguesRecursive(option.sequence, acceptChain, new HashSet<int>());
                }
                else
                {
                    CollectAcceptChainDialogues(option.sequence, acceptChain, visited);
                }
            }
        }
        
        private void CollectAcceptChainDialoguesRecursive(DialogueSequence dialogue, HashSet<DialogueSequence> acceptChain, HashSet<int> visited)
        {
            if (dialogue == null) return;
            
            int dialogueId = dialogue.GetInstanceID();
            if (visited.Contains(dialogueId)) return;
            visited.Add(dialogueId);
            
            if (dialogue.options == null) return;
            
            foreach (var option in dialogue.options)
            {
                if (option.sequence != null)
                {
                    acceptChain.Add(option.sequence);
                    CollectAcceptChainDialoguesRecursive(option.sequence, acceptChain, visited);
                }
            }
        }
        
        private bool IsDialogueInChain(DialogueSequence root, DialogueSequence target, HashSet<int> visited)
        {
            if (root == null || target == null)
                return false;
            
            if (root == target)
                return true;
            
            int rootId = root.GetInstanceID();
            if (visited.Contains(rootId))
                return false;
            
            visited.Add(rootId);
            
            if (root.options != null)
            {
                foreach (var option in root.options)
                {
                    if (option.sequence != null && IsDialogueInChain(option.sequence, target, visited))
                        return true;
                }
            }
            
            return false;
        }
        
        private void FindDialoguesFromQuestReferences(HashSet<DialogueSequence> allDialogues, Queue<DialogueSequence> dialoguesToProcess)
        {
            if (m_quest == null) return;
            
            string questPath = AssetDatabase.GetAssetPath(m_quest);
            if (string.IsNullOrEmpty(questPath)) return;
            
            string questName = m_quest.name.Replace("QUEST_", "");

            string questFolder = System.IO.Path.GetDirectoryName(questPath);
            string[] dialogueGuids = AssetDatabase.FindAssets("t:DialogueSequence", new[] { questFolder });

            foreach (string dialogueGuid in dialogueGuids)
            {
                string dialoguePath = AssetDatabase.GUIDToAssetPath(dialogueGuid);
                var dialogue = AssetDatabase.LoadAssetAtPath<DialogueSequence>(dialoguePath);
                
                if (dialogue != null && !allDialogues.Contains(dialogue))
                {
                    if (dialogue.name.Contains(questName))
                    {
                        allDialogues.Add(dialogue);
                        dialoguesToProcess.Enqueue(dialogue);
                    }
                    else
                    {
                    }
                }
            }

            FindDialoguesFromNPCInteractions(allDialogues, dialoguesToProcess);
        }
        
        private void FindDialoguesFromNPCInteractions(HashSet<DialogueSequence> allDialogues, Queue<DialogueSequence> dialoguesToProcess)
        {
            if (m_quest == null || m_quest.tasks == null) return;
            
            var taskGuids = new HashSet<string>();
            
            string questAssetPath = AssetDatabase.GetAssetPath(m_quest);
            string questGuid = AssetDatabase.AssetPathToGUID(questAssetPath);
            if (!string.IsNullOrEmpty(questGuid))
            {
                taskGuids.Add(questGuid);
            }
            
            foreach (var task in m_quest.tasks)
            {
                if (task == null) continue;
                string taskPath = AssetDatabase.GetAssetPath(task);
                string taskGuid = AssetDatabase.AssetPathToGUID(taskPath);
                if (!string.IsNullOrEmpty(taskGuid))
                {
                    taskGuids.Add(taskGuid);
                }
            }
            
            if (taskGuids.Count == 0) return;
            
            string[] allPrefabGuids = AssetDatabase.FindAssets("t:Prefab");
            
            int dialoguesFound = 0;
            
            foreach (string prefabGuid in allPrefabGuids)
            {
                string prefabPath = AssetDatabase.GUIDToAssetPath(prefabGuid);
                
                string[] deps = AssetDatabase.GetDependencies(prefabPath, false);
                bool referencesOurTask = false;
                foreach (string dep in deps)
                {
                    string depGuid = AssetDatabase.AssetPathToGUID(dep);
                    if (taskGuids.Contains(depGuid))
                    {
                        referencesOurTask = true;
                        break;
                    }
                }
                
                if (!referencesOurTask) continue;
                
                GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
                if (prefab == null) continue;
                
                var components = prefab.GetComponentsInChildren<MonoBehaviour>(true);
                
                foreach (var component in components)
                {
                    if (component == null) continue;
                    
                    ExtractDialoguesFromInteractions(component, allDialogues, dialoguesToProcess, taskGuids, ref dialoguesFound);
                }
            }
        }
        
        private void DiscoverAndCreateNPCConditionalInteractionNodes()
        {
            if (m_quest == null || m_quest.tasks == null) return;
            
            var taskToIndex = new Dictionary<Gyvr.Mythril2D.QuestTask, int>();
            var taskGuids = new HashSet<string>();
            var guidToTask = new Dictionary<string, Gyvr.Mythril2D.QuestTask>();
            
            string questAssetPath = AssetDatabase.GetAssetPath(m_quest);
            string questGuid = AssetDatabase.AssetPathToGUID(questAssetPath);
            if (!string.IsNullOrEmpty(questGuid))
            {
                taskGuids.Add(questGuid);
            }
            
            for (int i = 0; i < m_quest.tasks.Length; i++)
            {
                var task = m_quest.tasks[i];
                if (task == null) continue;
                
                taskToIndex[task] = i;
                string taskPath = AssetDatabase.GetAssetPath(task);
                string taskGuid = AssetDatabase.AssetPathToGUID(taskPath);
                if (!string.IsNullOrEmpty(taskGuid))
                {
                    taskGuids.Add(taskGuid);
                    guidToTask[taskGuid] = task;
                }
            }
            
            if (taskGuids.Count == 0) return;
            
            var createdNodes = new HashSet<string>();
            var prefabsToScan = new List<GameObject>();
            
            string[] allPrefabGuids = AssetDatabase.FindAssets("t:Prefab");
            
            foreach (string prefabGuid in allPrefabGuids)
            {
                string prefabPath = AssetDatabase.GUIDToAssetPath(prefabGuid);
                
                string[] deps = AssetDatabase.GetDependencies(prefabPath, false);
                bool referencesOurTask = false;
                foreach (string dep in deps)
                {
                    string depGuid = AssetDatabase.AssetPathToGUID(dep);
                    if (taskGuids.Contains(depGuid))
                    {
                        referencesOurTask = true;
                        break;
                    }
                }
                
                if (!referencesOurTask) continue;
                
                GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
                if (prefab == null) continue;
                
                var entityComp = prefab.GetComponent<Gyvr.Mythril2D.Entity>();
                
                if (prefab != null && entityComp != null)
                {
                    prefabsToScan.Add(prefab);
                }
            }
            
            float xOffset = 0;
            
            foreach (var prefab in prefabsToScan)
            {
                var entity = prefab.GetComponent<Gyvr.Mythril2D.Entity>();
                if (entity == null) continue;
                
                var conditionalInteractions = FindConditionalInteractionsOnEntity(entity, taskGuids, guidToTask);
                
                foreach (var (task, dialogue, interactionTypeName) in conditionalInteractions)
                {
                    if (task == null) continue;
                    
                    string nodeKey = $"npc-cond-{prefab.GetInstanceID()}-{task.GetInstanceID()}";
                    
                    bool nodeExists = m_nodeViews.Values.Any(n => 
                        n.userData is ConditionalNPCInteractionData data &&
                        data.npcPrefab == prefab &&
                        data.task == task);
                    
                    if (nodeExists || createdNodes.Contains(nodeKey)) continue;
                    createdNodes.Add(nodeKey);
                    
                    int taskIndex = taskToIndex.ContainsKey(task) ? taskToIndex[task] : 0;
                    string taskNodeId = $"task-{taskIndex}";
                    Vector2 position = new Vector2(200 + xOffset, 500 + (taskIndex * 150));
                    
                    if (m_nodeViews.TryGetValue(taskNodeId, out Node taskNode))
                    {
                        var taskPos = taskNode.GetPosition().position;
                        position = new Vector2(taskPos.x + 300 + xOffset, taskPos.y + 150);
                    }
                    
                    var node = CreateConditionalNPCInteractionNodeInternal(prefab, task, dialogue, position, null, interactionTypeName);
                    
                    if (node != null)
                    {
                        if (m_nodeViews.TryGetValue(taskNodeId, out taskNode))
                        {
                            var taskOutputPort = taskNode.Query<Port>().Where(p => 
                                p.direction == Direction.Output && 
                                p.portName != "Hint Override").First();
                            
                            var nodeInputPort = node.Query<Port>().Where(p => p.direction == Direction.Input).First();
                            
                            if (taskOutputPort != null && nodeInputPort != null && !IsConnected(taskOutputPort, nodeInputPort))
                            {
                                var edge = taskOutputPort.ConnectTo(nodeInputPort);
                                edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                AddElement(edge);
                            }
                        }
                        
                        if (dialogue != null)
                        {
                            string dialogueNodeId = $"dialogue-{dialogue.GetInstanceID()}";
                            
                            if (m_nodeViews.TryGetValue(dialogueNodeId, out Node dialogueNode))
                            {
                                var nodeOutputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
                                var dialogueInputPort = dialogueNode.Query<Port>().Where(p => p.direction == Direction.Input).First();
                                
                                if (nodeOutputPort != null && dialogueInputPort != null && !IsConnected(nodeOutputPort, dialogueInputPort))
                                {
                                    var edge = nodeOutputPort.ConnectTo(dialogueInputPort);
                                    edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                    edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                    AddElement(edge);
                                }
                            }
                            else
                            {
                                var matchingNode = m_nodeViews.Values.FirstOrDefault(n => n.userData is UnityEngine.Object obj && obj == dialogue);
                                if (matchingNode != null)
                                {
                                    var nodeOutputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
                                    var dialogueInputPort = matchingNode.Query<Port>().Where(p => p.direction == Direction.Input).First();
                                    
                                    if (nodeOutputPort != null && dialogueInputPort != null && !IsConnected(nodeOutputPort, dialogueInputPort))
                                    {
                                        var edge = nodeOutputPort.ConnectTo(dialogueInputPort);
                                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                                        AddElement(edge);
                                    }
                                }
                            }
                        }
                        
                        xOffset += 50;
                    }
                }
            }
        }
        
        private void WireConditionalNPCInteractionDialogues()
        {
            var dialogueNodes = new Dictionary<DialogueSequence, Node>();
            foreach (var kvp in m_nodeViews)
            {
                if (kvp.Value.userData is DialogueSequence dialogue && dialogue != null)
                {
                    dialogueNodes[dialogue] = kvp.Value;
                }
            }

            foreach (var kvp in m_nodeViews)
            {
                var node = kvp.Value;
                if (!(node.userData is ConditionalNPCInteractionData data)) continue;
                if (data.dialogue == null) continue;
                if (data.interactionType != "DialogueInteraction") continue;
                
                string dialogueNodeId = $"dialogue-{data.dialogue.GetInstanceID()}";
                Node dialogueNode = null;
                
                if (!m_nodeViews.TryGetValue(dialogueNodeId, out dialogueNode))
                    dialogueNode = m_nodeViews.Values.FirstOrDefault(n => n.userData is UnityEngine.Object obj && obj == data.dialogue);
                
                if (dialogueNode == null) continue;
                
                var outputPort = node.Query<Port>().Where(p => p.direction == Direction.Output).First();
                var inputPort = dialogueNode.Query<Port>().Where(p => p.direction == Direction.Input).First();
                
                if (outputPort != null && inputPort != null && !IsConnected(outputPort, inputPort))
                {
                    var edge = outputPort.ConnectTo(inputPort);
                    edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                    edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                    AddElement(edge);
                }

                WireDialogueOptionsBFS(data.dialogue, dialogueNodes);
            }
        }

        private void WireDialogueOptionsBFS(DialogueSequence rootDialogue, Dictionary<DialogueSequence, Node> dialogueNodes)
        {
            var visited = new HashSet<DialogueSequence>();
            var queue = new Queue<DialogueSequence>();
            queue.Enqueue(rootDialogue);
            visited.Add(rootDialogue);

            while (queue.Count > 0)
            {
                var dialogue = queue.Dequeue();
                if (dialogue.options == null) continue;
                
                if (!dialogueNodes.TryGetValue(dialogue, out Node sourceNode)) continue;

                for (int i = 0; i < dialogue.options.Length; i++)
                {
                    var option = dialogue.options[i];
                    if (option.sequence == null) continue;
                    
                    if (!dialogueNodes.TryGetValue(option.sequence, out Node targetNode)) continue;

                    string portName = $"option-{i}";
                    var outputPort = FindPort(sourceNode, Direction.Output, portName);
                    var inputPortTarget = FindPort(targetNode, Direction.Input, "");

                    if (outputPort != null && inputPortTarget != null && !IsConnected(outputPort, inputPortTarget))
                    {
                        var edge = outputPort.ConnectTo(inputPortTarget);
                        edge.edgeControl.inputColor = new Color(1f, 0.6f, 0.2f);
                        edge.edgeControl.outputColor = new Color(1f, 0.6f, 0.2f);
                        AddElement(edge);
                    }

                    if (!visited.Contains(option.sequence))
                    {
                        visited.Add(option.sequence);
                        queue.Enqueue(option.sequence);
                    }
                }
            }
        }
        
        private List<(Gyvr.Mythril2D.QuestTask, Gyvr.Mythril2D.DialogueSequence, string)> FindConditionalInteractionsOnEntity(
            Gyvr.Mythril2D.Entity entity, 
            HashSet<string> taskGuids, 
            Dictionary<string, Gyvr.Mythril2D.QuestTask> guidToTask)
        {
            var results = new List<(Gyvr.Mythril2D.QuestTask, Gyvr.Mythril2D.DialogueSequence, string)>();
            
            if (entity == null || entity.gameObject == null) return results;
            
            var currentEntityType = entity.GetType();
            while (currentEntityType != null && currentEntityType != typeof(UnityEngine.Object))
            {
                var directInteractionField = currentEntityType.GetField("m_interaction", 
                    System.Reflection.BindingFlags.NonPublic | 
                    System.Reflection.BindingFlags.Instance | 
                    System.Reflection.BindingFlags.DeclaredOnly);
                
                if (directInteractionField != null)
                {
                    var directInteraction = directInteractionField.GetValue(entity);
                    
                    if (directInteraction != null)
                    {
                        SearchForConditionalInteractions(directInteraction, taskGuids, guidToTask, results);
                    }
                    break;
                }
                currentEntityType = currentEntityType.BaseType;
            }
            
            var components = entity.gameObject.GetComponentsInChildren<MonoBehaviour>(true);
            
            foreach (var component in components)
            {
                if (component == null) continue;
                if (component == entity) continue;
                
                var compType = component.GetType();
                while (compType != null && compType != typeof(UnityEngine.Object))
                {
                    var fields = compType.GetFields(
                        System.Reflection.BindingFlags.NonPublic | 
                        System.Reflection.BindingFlags.Instance | 
                        System.Reflection.BindingFlags.DeclaredOnly);
                    
                    foreach (var field in fields)
                    {
                        bool isInteractionField = field.Name == "m_interaction" || 
                            typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType);
                        
                        if (isInteractionField)
                        {
                            var interaction = field.GetValue(component);
                            if (interaction != null)
                            {
                                SearchForConditionalInteractions(interaction, taskGuids, guidToTask, results);
                            }
                        }
                    }
                    
                    compType = compType.BaseType;
                }
            }
            
            return results;
        }
        
        private void SearchForConditionalInteractions(
            object obj,
            HashSet<string> taskGuids,
            Dictionary<string, Gyvr.Mythril2D.QuestTask> guidToTask,
            List<(Gyvr.Mythril2D.QuestTask, Gyvr.Mythril2D.DialogueSequence, string)> results)
        {
            if (obj == null) return;
            
            var type = obj.GetType();
            
            if (type.Name == "ConditionalInteraction")
            {
                var conditionField = type.GetField("m_condition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                var interactionField = type.GetField("m_interaction", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                
                if (conditionField != null && interactionField != null)
                {
                    var condition = conditionField.GetValue(obj);
                    var innerInteraction = interactionField.GetValue(obj);
                    
                    Gyvr.Mythril2D.QuestTask matchedTask = null;
                    bool isRelevant = false;
                    
                    if (condition != null)
                    {
                        string conditionTypeName = condition.GetType().Name;
                        
                        if (conditionTypeName == "IsQuestTaskActive" || conditionTypeName == "IsQuestTaskInState")
                        {
                            var taskField = condition.GetType().GetField("m_questTask", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            if (taskField == null)
                            {
                                taskField = condition.GetType().GetField("m_task", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            }
                            
                            if (taskField != null)
                            {
                                var task = taskField.GetValue(condition) as Gyvr.Mythril2D.QuestTask;
                                if (task != null)
                                {
                                    string taskPath = AssetDatabase.GetAssetPath(task);
                                    string taskGuid = AssetDatabase.AssetPathToGUID(taskPath);
                                    
                                    if (taskGuids.Contains(taskGuid))
                                    {
                                        matchedTask = task;
                                        isRelevant = true;
                                    }
                                }
                            }
                        }
                        else if (conditionTypeName == "IsQuestInState")
                        {
                            var questField = condition.GetType().GetField("m_quest", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            if (questField != null)
                            {
                                var quest = questField.GetValue(condition) as Gyvr.Mythril2D.Quest;
                                if (quest != null)
                                {
                                    bool matches = (quest == m_quest);
                                    if (!matches)
                                    {
                                        string condQuestPath = AssetDatabase.GetAssetPath(quest);
                                        string condQuestGuid = AssetDatabase.AssetPathToGUID(condQuestPath);
                                        matches = taskGuids.Contains(condQuestGuid);
                                    }
                                    if (matches)
                                    {
                                        isRelevant = true;
                                        if (m_quest.tasks != null && m_quest.tasks.Length > 0)
                                            matchedTask = m_quest.tasks[0];
                                    }
                                }
                            }
                        }
                        else if (conditionTypeName == "IsGameFlagSet")
                        {
                            isRelevant = true;
                            if (m_quest.tasks != null && m_quest.tasks.Length > 0)
                                matchedTask = m_quest.tasks[0];
                        }
                    }
                    
                    if (isRelevant)
                    {
                        Gyvr.Mythril2D.DialogueSequence dialogue = null;
                        string interactionTypeName = innerInteraction?.GetType().Name ?? "DialogueInteraction";
                        
                        if (innerInteraction != null)
                        {
                            string[] dialogueFieldNames = { "m_sequence", "m_dialogue", "m_dialogueIfCanPay" };
                            foreach (var fieldName in dialogueFieldNames)
                            {
                                var seqField = innerInteraction.GetType().GetField(fieldName, 
                                    System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                                if (seqField != null && seqField.FieldType == typeof(Gyvr.Mythril2D.DialogueSequence))
                                {
                                    dialogue = seqField.GetValue(innerInteraction) as Gyvr.Mythril2D.DialogueSequence;
                                    if (dialogue != null) break;
                                }
                            }
                        }
                        
                        results.Add((matchedTask, dialogue, interactionTypeName));
                    }
                }
                return;
            }
            
            var currentType = type;
            while (currentType != null && currentType != typeof(UnityEngine.Object))
            {
                var fields = currentType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                
                foreach (var field in fields)
                {
                    if (typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType))
                    {
                        var childInteraction = field.GetValue(obj);
                        if (childInteraction != null)
                        {
                            SearchForConditionalInteractions(childInteraction, taskGuids, guidToTask, results);
                        }
                    }
                    else if (field.FieldType.IsArray && field.FieldType.GetElementType() != null && typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType.GetElementType()))
                    {
                        var array = field.GetValue(obj) as System.Array;
                        if (array != null)
                        {
                            foreach (var item in array)
                            {
                                SearchForConditionalInteractions(item, taskGuids, guidToTask, results);
                            }
                        }
                    }
                }
                currentType = currentType.BaseType;
            }
        }
        
        private void ExtractDialoguesFromInteractions(object obj, HashSet<DialogueSequence> allDialogues, Queue<DialogueSequence> dialoguesToProcess, HashSet<string> taskGuids, ref int dialoguesFound)
        {
            if (obj == null) return;
            
            var type = obj.GetType();
            
            if (type.Name == "ConditionalInteraction")
            {
                var conditionField = type.GetField("m_condition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                var interactionField = type.GetField("m_interaction", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                
                if (conditionField != null && interactionField != null)
                {
                    var condition = conditionField.GetValue(obj);
                    var interaction = interactionField.GetValue(obj);
                    
                    bool shouldExtractDialogue = false;
                    
                    if (condition != null)
                    {
                        string conditionTypeName = condition.GetType().Name;
                        
                        if (conditionTypeName == "IsQuestTaskActive" || conditionTypeName == "IsQuestTaskInState")
                        {
                            var taskField = condition.GetType().GetField("m_questTask", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            if (taskField == null)
                            {
                                taskField = condition.GetType().GetField("m_task", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            }
                            
                            if (taskField != null)
                            {
                                var task = taskField.GetValue(condition) as Gyvr.Mythril2D.QuestTask;
                                if (task != null)
                                {
                                    string taskPath = AssetDatabase.GetAssetPath(task);
                                    string taskGuid = AssetDatabase.AssetPathToGUID(taskPath);
                                    
                                    if (taskGuids.Contains(taskGuid))
                                    {
                                        shouldExtractDialogue = true;
                                    }
                                }
                            }
                        }
                        else if (conditionTypeName == "IsQuestInState")
                        {
                            var questField = condition.GetType().GetField("m_quest", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                            if (questField != null)
                            {
                                var quest = questField.GetValue(condition) as Gyvr.Mythril2D.Quest;
                                if (quest != null)
                                {
                                    if (quest == m_quest)
                                    {
                                        shouldExtractDialogue = true;
                                    }
                                    else
                                    {
                                        string condQuestPath = AssetDatabase.GetAssetPath(quest);
                                        string condQuestGuid = AssetDatabase.AssetPathToGUID(condQuestPath);
                                        if (taskGuids.Contains(condQuestGuid))
                                        {
                                            shouldExtractDialogue = true;
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            shouldExtractDialogue = true;
                        }
                    }
                    
                    if (shouldExtractDialogue && interaction != null)
                    {
                        ExtractDialogueFromInteraction(interaction, allDialogues, dialoguesToProcess, ref dialoguesFound);
                    }
                }
                return;
            }
            
            var currentType = type;
            while (currentType != null && currentType != typeof(UnityEngine.Object))
            {
                var fields = currentType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                foreach (var field in fields)
                {
                    if (typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType))
                    {
                        var interaction = field.GetValue(obj);
                        if (interaction != null)
                        {
                            ExtractDialoguesFromInteractions(interaction, allDialogues, dialoguesToProcess, taskGuids, ref dialoguesFound);
                        }
                    }
                    else if (field.FieldType.IsArray && field.FieldType.GetElementType() != null && typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType.GetElementType()))
                    {
                        var array = field.GetValue(obj) as System.Array;
                        if (array != null)
                        {
                            foreach (var item in array)
                            {
                                ExtractDialoguesFromInteractions(item, allDialogues, dialoguesToProcess, taskGuids, ref dialoguesFound);
                            }
                        }
                    }
                    else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List<>))
                    {
                        var elementType = field.FieldType.GetGenericArguments()[0];
                        if (typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(elementType))
                        {
                            var list = field.GetValue(obj) as System.Collections.IList;
                            if (list != null)
                            {
                                foreach (var item in list)
                                {
                                    ExtractDialoguesFromInteractions(item, allDialogues, dialoguesToProcess, taskGuids, ref dialoguesFound);
                                }
                            }
                        }
                    }
                }
                currentType = currentType.BaseType;
            }
        }
        
        private void ExtractDialogueFromInteraction(object interaction, HashSet<DialogueSequence> allDialogues, Queue<DialogueSequence> dialoguesToProcess, ref int dialoguesFound)
        {
            if (interaction == null) return;
            
            var type = interaction.GetType();
            
            string[] dialogueFieldNames = { "m_sequence", "m_dialogue", "m_dialogueIfCanPay", "m_dialogueIfCannotPay" };
            
            foreach (var fieldName in dialogueFieldNames)
            {
                var field = type.GetField(fieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                if (field != null && field.FieldType == typeof(DialogueSequence))
                {
                    var dialogue = field.GetValue(interaction) as DialogueSequence;
                    if (dialogue != null && !allDialogues.Contains(dialogue))
                    {
                        allDialogues.Add(dialogue);
                        dialoguesToProcess.Enqueue(dialogue);
                        dialoguesFound++;
                    }
                }
            }
            
            var fields = type.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            foreach (var field in fields)
            {
                if (typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType))
                {
                    var childInteraction = field.GetValue(interaction);
                    ExtractDialogueFromInteraction(childInteraction, allDialogues, dialoguesToProcess, ref dialoguesFound);
                }
                else if (field.FieldType.IsArray && field.FieldType.GetElementType() != null && typeof(Gyvr.Mythril2D.IInteraction).IsAssignableFrom(field.FieldType.GetElementType()))
                {
                    var array = field.GetValue(interaction) as System.Array;
                    if (array != null)
                    {
                        foreach (var item in array)
                        {
                            ExtractDialogueFromInteraction(item, allDialogues, dialoguesToProcess, ref dialoguesFound);
                        }
                    }
                }
            }
        }
        
        private bool IsConnected(Port outputPort, Port inputPort)
        {
            foreach (var edge in edges.ToList())
            {
                if (edge.output == outputPort && edge.input == inputPort)
                    return true;
            }
            return false;
        }

        private Port FindPort(Node node, Direction direction, string portName)
        {
            var directPorts = node.Query<Port>().ToList();
            foreach (var port in directPorts)
            {
                if (port.direction == direction && port.portName == portName)
                    return port;
            }

            var container = direction == Direction.Output ? node.outputContainer : node.inputContainer;
            var containerPorts = container.Query<Port>().ToList();
            foreach (var port in containerPorts)
            {
                if (port.portName == portName)
                    return port;
            }

            return null;
        }

        private Node CreateNodeFromData(NodeData data)
        {
            
            switch (data.nodeType)
            {
                case "QuestInfo":
                    var questInfoNode = CreateQuestInfoNode(data.position, data.nodeId);
                    return questInfoNode;

                case "Task":
                    if (int.TryParse(data.dataReference, out int taskIndex))
                    {
                        var taskNode = CreateTaskNode(taskIndex, data.position, data.nodeId);
                        return taskNode;
                    }
                    break;
                
                case "Dialogue":
                    string dialogueName = data.dataReference;
                    Gyvr.Mythril2D.DialogueSequence dialogue = null;
                    string effectiveRole = data.role;
                    
                    if (data.role == "QuestOffer" && m_quest.questOfferDialogue != null)
                    {
                        if (string.IsNullOrEmpty(dialogueName) || m_quest.questOfferDialogue.name == dialogueName)
                        {
                            dialogue = m_quest.questOfferDialogue;
                        }
                        else
                        {
                            effectiveRole = "";
                        }
                    }
                    else if (data.role == "QuestHint" && m_quest.questHintDialogue != null)
                    {
                        if (string.IsNullOrEmpty(dialogueName) || m_quest.questHintDialogue.name == dialogueName)
                        {
                            dialogue = m_quest.questHintDialogue;
                        }
                        else
                        {
                            effectiveRole = "";
                        }
                    }
                    else if (data.role == "QuestCompleted" && m_quest.questCompletedDialogue != null)
                    {
                        if (string.IsNullOrEmpty(dialogueName) || m_quest.questCompletedDialogue.name == dialogueName)
                        {
                            dialogue = m_quest.questCompletedDialogue;
                        }
                        else
                        {
                            effectiveRole = "";
                        }
                    }
                    
                    if (dialogue == null && int.TryParse(dialogueName, out int dialogueInstanceId))
                    {
#pragma warning disable CS0618
                        dialogue = EditorUtility.InstanceIDToObject(dialogueInstanceId) as Gyvr.Mythril2D.DialogueSequence;
#pragma warning restore CS0618
                    }
                    
                    if (dialogue == null && m_quest != null && !string.IsNullOrEmpty(dialogueName))
                    {
                        string questPath = AssetDatabase.GetAssetPath(m_quest);
                        string questFolder = System.IO.Path.GetDirectoryName(questPath);
                        string questName = m_quest.name.Replace("QUEST_", "");
                        
                        if (dialogueName.Contains(questName))
                        {
                            string[] guids = AssetDatabase.FindAssets($"t:DialogueSequence {dialogueName}", new[] { questFolder });
                            foreach (string guid in guids)
                            {
                                string path = AssetDatabase.GUIDToAssetPath(guid);
                                var dlg = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.DialogueSequence>(path);
                                if (dlg != null && dlg.name == dialogueName)
                                {
                                    dialogue = dlg;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            return null;
                        }
                    }
                    
                    if (dialogue != null)
                    {
                        string label;
                        
                        string customTitle = null;
                        if (data.metadata != null)
                        {
                            var titleMeta = data.metadata.Find(m => m.key == "customTitle");
                            if (titleMeta != null && !string.IsNullOrEmpty(titleMeta.value))
                                customTitle = titleMeta.value;
                        }
                        
                        if (!string.IsNullOrEmpty(customTitle))
                            label = customTitle;
                        else if (effectiveRole == "QuestOffer")
                            label = "Quest Offer";
                        else if (effectiveRole == "QuestHint")
                            label = "Quest Hint (Default)";
                        else if (effectiveRole == "QuestCompleted")
                            label = "Quest Completed";
                        else if (effectiveRole == "HintOverride_Unconnected" || effectiveRole.StartsWith("HintOverride_"))
                            label = "Hint Override";
                        else
                        {
                            var allDialogues = CollectAllDialogues();
                            label = GetDialogueLabelFromParent(dialogue, allDialogues);
                        }
                        
                        var dialogueNode = CreateDialogueNode(dialogue, label, data.position, data.nodeId, effectiveRole);
                        return dialogueNode;
                    }
                    else
                    {
                    }
                    break;
                
                case "NPCTaskDialogue":
                case "ConditionalNPCInteraction":
                    GameObject npcPrefab = null;
                    Gyvr.Mythril2D.QuestTask savedTask = null;
                    Gyvr.Mythril2D.DialogueSequence savedDialogue = null;
                    string savedInteractionType = "DialogueInteraction";
                    string savedConditionType = "IsQuestTaskActive";
                    Gyvr.Mythril2D.Quest savedConditionQuest = null;
                    string savedConditionQuestState = "Active";
                    string savedConditionGameFlag = "";
                    Gyvr.Mythril2D.Quest savedInteractionQuest = null;
                    string savedCommandType = "SetGameFlag";
                    string savedInterruptionPolicy = "OnSuccess";
                    
                    if (data.metadata != null)
                    {
                        var interactionTypeMeta = data.metadata.Find(m => m.key == "interactionType");
                        if (interactionTypeMeta != null && !string.IsNullOrEmpty(interactionTypeMeta.value))
                        {
                            savedInteractionType = interactionTypeMeta.value;
                        }
                        
                        var conditionTypeMeta = data.metadata.Find(m => m.key == "conditionType");
                        if (conditionTypeMeta != null && !string.IsNullOrEmpty(conditionTypeMeta.value))
                        {
                            savedConditionType = conditionTypeMeta.value;
                        }
                        
                        var conditionQuestStateMeta = data.metadata.Find(m => m.key == "conditionQuestState");
                        if (conditionQuestStateMeta != null && !string.IsNullOrEmpty(conditionQuestStateMeta.value))
                        {
                            savedConditionQuestState = conditionQuestStateMeta.value;
                        }
                        
                        var conditionGameFlagMeta = data.metadata.Find(m => m.key == "conditionGameFlag");
                        if (conditionGameFlagMeta != null && !string.IsNullOrEmpty(conditionGameFlagMeta.value))
                        {
                            savedConditionGameFlag = conditionGameFlagMeta.value;
                        }
                        
                        var commandTypeMeta = data.metadata.Find(m => m.key == "commandType");
                        if (commandTypeMeta != null && !string.IsNullOrEmpty(commandTypeMeta.value))
                        {
                            savedCommandType = commandTypeMeta.value;
                        }
                        
                        var interruptionPolicyMeta = data.metadata.Find(m => m.key == "interruptionPolicy");
                        if (interruptionPolicyMeta != null && !string.IsNullOrEmpty(interruptionPolicyMeta.value))
                        {
                            savedInterruptionPolicy = interruptionPolicyMeta.value;
                        }
                        
                        var npcMeta = data.metadata.Find(m => m.key == "npcGuid");
                        if (npcMeta != null && !string.IsNullOrEmpty(npcMeta.value))
                        {
                            string npcPath = AssetDatabase.GUIDToAssetPath(npcMeta.value);
                            npcPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(npcPath);
                        }
                        
                        var taskMeta = data.metadata.Find(m => m.key == "taskGuid");
                        if (taskMeta != null && !string.IsNullOrEmpty(taskMeta.value))
                        {
                            string taskPath = AssetDatabase.GUIDToAssetPath(taskMeta.value);
                            savedTask = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.QuestTask>(taskPath);
                        }
                        
                        var dialogueMeta = data.metadata.Find(m => m.key == "dialogueGuid");
                        if (dialogueMeta != null && !string.IsNullOrEmpty(dialogueMeta.value))
                        {
                            string dialoguePath = AssetDatabase.GUIDToAssetPath(dialogueMeta.value);
                            savedDialogue = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.DialogueSequence>(dialoguePath);
                        }
                        
                        var conditionQuestMeta = data.metadata.Find(m => m.key == "conditionQuestGuid");
                        if (conditionQuestMeta != null && !string.IsNullOrEmpty(conditionQuestMeta.value))
                        {
                            string questPath = AssetDatabase.GUIDToAssetPath(conditionQuestMeta.value);
                            savedConditionQuest = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.Quest>(questPath);
                        }
                        
                        var interactionQuestMeta = data.metadata.Find(m => m.key == "interactionQuestGuid");
                        if (interactionQuestMeta != null && !string.IsNullOrEmpty(interactionQuestMeta.value))
                        {
                            string interactionQuestPath = AssetDatabase.GUIDToAssetPath(interactionQuestMeta.value);
                            savedInteractionQuest = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.Quest>(interactionQuestPath);
                        }
                        
                        // Load command-specific fields
                        var savedCommandData = new ConditionalNPCInteractionData();
                        
                        var flagIdMeta = data.metadata.Find(m => m.key == "commandFlagId");
                        if (flagIdMeta != null) savedCommandData.commandFlagId = flagIdMeta.value ?? "";
                        
                        var flagStateMeta = data.metadata.Find(m => m.key == "commandFlagState");
                        if (flagStateMeta != null) bool.TryParse(flagStateMeta.value, out savedCommandData.commandFlagState);
                        
                        var quantityMeta = data.metadata.Find(m => m.key == "commandQuantity");
                        if (quantityMeta != null) int.TryParse(quantityMeta.value, out savedCommandData.commandQuantity);
                        
                        var isAddMeta = data.metadata.Find(m => m.key == "commandIsAdd");
                        if (isAddMeta != null) bool.TryParse(isAddMeta.value, out savedCommandData.commandIsAdd);
                        
                        var moneyMeta = data.metadata.Find(m => m.key == "commandMoneyAmount");
                        if (moneyMeta != null) int.TryParse(moneyMeta.value, out savedCommandData.commandMoneyAmount);
                        
                        var expMeta = data.metadata.Find(m => m.key == "commandExperience");
                        if (expMeta != null) int.TryParse(expMeta.value, out savedCommandData.commandExperience);
                        
                        var waitMeta = data.metadata.Find(m => m.key == "commandWaitTime");
                        if (waitMeta != null) float.TryParse(waitMeta.value, out savedCommandData.commandWaitTime);
                        
                        var cmdItemMeta = data.metadata.Find(m => m.key == "commandItemGuid");
                        if (cmdItemMeta != null && !string.IsNullOrEmpty(cmdItemMeta.value))
                        {
                            string itemPath = AssetDatabase.GUIDToAssetPath(cmdItemMeta.value);
                            savedCommandData.commandItem = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.Item>(itemPath);
                        }
                        
                        var cmdTaskMeta = data.metadata.Find(m => m.key == "commandTaskGuid");
                        if (cmdTaskMeta != null && !string.IsNullOrEmpty(cmdTaskMeta.value))
                        {
                            string cmdTaskPath = AssetDatabase.GUIDToAssetPath(cmdTaskMeta.value);
                            savedCommandData.commandTask = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.QuestTask>(cmdTaskPath);
                        }
                        
                        var cmdQuestMeta = data.metadata.Find(m => m.key == "commandQuestGuid");
                        if (cmdQuestMeta != null && !string.IsNullOrEmpty(cmdQuestMeta.value))
                        {
                            string cmdQuestPath = AssetDatabase.GUIDToAssetPath(cmdQuestMeta.value);
                            savedCommandData.commandQuest = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.Quest>(cmdQuestPath);
                        }
                        
                        var cmdDialogueMeta = data.metadata.Find(m => m.key == "commandDialogueGuid");
                        if (cmdDialogueMeta != null && !string.IsNullOrEmpty(cmdDialogueMeta.value))
                        {
                            string cmdDialoguePath = AssetDatabase.GUIDToAssetPath(cmdDialogueMeta.value);
                            savedCommandData.commandDialogue = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.DialogueSequence>(cmdDialoguePath);
                        }
                        
                        var conditionalNpcNode = CreateConditionalNPCInteractionNodeInternal(npcPrefab, savedTask, savedDialogue, data.position, data.nodeId, savedInteractionType, savedConditionType, savedConditionQuest, savedConditionQuestState, savedConditionGameFlag, savedInteractionQuest, savedCommandType, savedInterruptionPolicy, savedCommandData);
                        return conditionalNpcNode;
                    }
                    
                    var conditionalNpcNodeDefault = CreateConditionalNPCInteractionNodeInternal(npcPrefab, savedTask, savedDialogue, data.position, data.nodeId, savedInteractionType, savedConditionType, savedConditionQuest, savedConditionQuestState, savedConditionGameFlag, savedInteractionQuest, savedCommandType, savedInterruptionPolicy);
                    return conditionalNpcNodeDefault;
            }

            return null;
        }

        private void RenameDialogueAssets(Gyvr.Mythril2D.Quest quest, string newQuestName)
        {
            string sanitizedName = newQuestName.Replace(" ", "_");
            
            string questAssetPath = AssetDatabase.GetAssetPath(quest);
            
            var allAssets = AssetDatabase.LoadAllAssetsAtPath(questAssetPath);
            
            foreach (var asset in allAssets)
            {
                if (asset is Gyvr.Mythril2D.DialogueSequence dialogue)
                {
                    string oldName = dialogue.name;
                    string newDialogueName = oldName;
                    
                    if (oldName.Contains("_Offer"))
                    {
                        newDialogueName = $"DIAL_{sanitizedName}_Offer";
                    }
                    else if (oldName.Contains("_Hint"))
                    {
                        newDialogueName = $"DIAL_{sanitizedName}_Hint";
                    }
                    else if (oldName.Contains("_Completed"))
                    {
                        newDialogueName = $"DIAL_{sanitizedName}_Completed";
                    }
                    else if (oldName.Contains("HintOverride"))
                    {
                        continue;
                    }
                    
                    if (newDialogueName != oldName)
                    {
                        dialogue.name = newDialogueName;
                        EditorUtility.SetDirty(dialogue);
                    }
                }
            }
        }

        private Node CreateQuestInfoNode(Vector2 position, string nodeId = null)
        {
            if (nodeId == null)
            {
                nodeId = "quest-info";
            }

            var node = new Node
            {
                title = "Quest Info",
                name = nodeId
            };

            node.SetPosition(new Rect(position, Vector2.zero));
            
            node.capabilities &= ~Capabilities.Collapsible;
            node.capabilities &= ~Capabilities.Deletable;
            var titleButtonsContainer = node.Q("title-button-container");
            if (titleButtonsContainer != null)
                titleButtonsContainer.style.display = DisplayStyle.None;
            
            node.inputContainer.style.display = DisplayStyle.None;
            node.outputContainer.style.display = DisplayStyle.None;
            
            node.RegisterCallback<MouseDownEvent>(evt =>
            {
                if (evt.button == 0)
                {
                    Selection.activeObject = m_quest;
                }
            });
            
            node.mainContainer.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 1f);
            node.mainContainer.style.minWidth = 300;
            node.mainContainer.style.maxWidth = 500;
            node.style.overflow = Overflow.Hidden;
            
            node.style.borderBottomWidth = 0;
            node.style.borderLeftWidth = 0;
            node.style.borderRightWidth = 0;
            node.style.borderTopWidth = 1;
            node.style.paddingBottom = 0;
            node.style.paddingLeft = 0;
            node.style.paddingRight = 0;
            node.style.paddingTop = 0;
            node.style.overflow = Overflow.Hidden;
            
            node.style.borderBottomWidth = 1;
            node.style.borderLeftWidth = 1;
            node.style.borderRightWidth = 1;
            node.style.borderTopWidth = 1;
            node.style.paddingBottom = 0;
            node.style.paddingLeft = 0;
            node.style.paddingRight = 0;
            node.style.paddingTop = 0;
            node.style.marginTop = 1;
            node.style.marginBottom = 1;
            node.style.marginLeft = 1;
            node.style.marginRight = 1;

            var titleContainer = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    alignItems = Align.Center,
                    paddingTop = 6,
                    paddingBottom = 6,
                    paddingLeft = 8
                }
            };
            
            var titleLabel = new Label(m_quest.title)
            {
                name = "quest-title-label",
                style = { 
                    fontSize = 14,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    unityTextAlign = TextAnchor.MiddleLeft,
                    flexGrow = 1
                }
            };
            
            titleLabel.RegisterCallback<MouseDownEvent>(evt =>
            {
                evt.StopPropagation();
                if (evt.clickCount == 1)
                {
                    var titleField = new TextField { value = m_quest.title };
                    titleField.style.fontSize = 14;
                    titleField.style.unityFontStyleAndWeight = FontStyle.Bold;
                    titleField.style.paddingTop = 5;
                    titleField.style.paddingBottom = 5;
                    
                    var parent = titleLabel.parent;
                    int index = parent.IndexOf(titleLabel);
                    parent.Remove(titleLabel);
                    parent.Insert(index, titleField);
                    
                    titleField.Focus();
                    titleField.SelectAll();
                    
                    titleField.RegisterCallback<BlurEvent>(blurEvt =>
                    {
                        SaveTitleAndRevertToLabel(titleField, parent, index);
                    });
                    
                    titleField.RegisterCallback<KeyDownEvent>(keyEvt =>
                    {
                        if (keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter)
                        {
                            SaveTitleAndRevertToLabel(titleField, parent, index);
                            keyEvt.StopPropagation();
                        }
                        else if (keyEvt.keyCode == KeyCode.Escape)
                        {
                            parent.Remove(titleField);
                            parent.Insert(index, titleLabel);
                            keyEvt.StopPropagation();
                        }
                    });
                }
            });
            
            titleContainer.Add(titleLabel);
            node.mainContainer.Add(titleContainer);
            
            void SaveTitleAndRevertToLabel(TextField field, VisualElement parent, int index)
            {
                if (!string.IsNullOrWhiteSpace(field.value) && field.value != m_quest.title)
                {
                    var serializedQuest = new SerializedObject(m_quest);
                    var titleProperty = serializedQuest.FindProperty("m_title");
                    titleProperty.stringValue = field.value;
                    serializedQuest.ApplyModifiedProperties();
                    
                    string assetPath = AssetDatabase.GetAssetPath(m_quest);
                    string directory = System.IO.Path.GetDirectoryName(assetPath);
                    string extension = System.IO.Path.GetExtension(assetPath);
                    string newFileName = $"QUEST_{field.value.Replace(" ", "_")}";
                    
                    string result = AssetDatabase.RenameAsset(assetPath, newFileName);
                    if (string.IsNullOrEmpty(result))
                    {
                        
                        RenameDialogueAssets(m_quest, field.value);
                        
                        string graphPath = assetPath.Replace(extension, ".graph");
                        if (System.IO.File.Exists(graphPath))
                        {
                            AssetDatabase.RenameAsset(graphPath, newFileName);
                        }
                        AssetDatabase.SaveAssets();
                    }
                    
                    titleLabel.text = field.value;
                }
                
                parent.Remove(field);
                parent.Insert(index, titleLabel);
            }
            
            string displayDesc = string.IsNullOrWhiteSpace(m_quest.description) ? "[Edit description]" : m_quest.description;
            var descLabel = new Label(displayDesc)
            {
                name = "quest-desc-label",
                style = { 
                    whiteSpace = WhiteSpace.Normal,
                    maxWidth = 280,
                    fontSize = 12,
                    paddingBottom = 10,
                    paddingTop = 5,
                    paddingLeft = 8,
                    paddingRight = 8,
                    color = new Color(1f, 1f, 1f, string.IsNullOrWhiteSpace(m_quest.description) ? 0.4f : 1f)
                }
            };
            
            descLabel.RegisterCallback<MouseDownEvent>(evt =>
            {
                evt.StopPropagation();
                if (evt.clickCount == 1)
                {
                    var descField = new TextField { value = m_quest.description, multiline = true };
                    descField.style.whiteSpace = WhiteSpace.Normal;
                    descField.style.maxWidth = 250;
                    descField.style.minHeight = 60;
                    descField.style.fontSize = 10;
                    descField.style.paddingBottom = 5;
                    
                    var parent = descLabel.parent;
                    int index = parent.IndexOf(descLabel);
                    parent.Remove(descLabel);
                    parent.Insert(index, descField);
                    
                    descField.Focus();
                    
                    descField.RegisterCallback<BlurEvent>(blurEvt =>
                    {
                        SaveDescAndRevertToLabel(descField, parent, index);
                    });
                }
            });
            
            void SaveDescAndRevertToLabel(TextField field, VisualElement parent, int index)
            {
                if (field.value != m_quest.description)
                {
                    var serializedQuest = new SerializedObject(m_quest);
                    var descProperty = serializedQuest.FindProperty("m_description");
                    descProperty.stringValue = field.value;
                    serializedQuest.ApplyModifiedProperties();
                    AssetDatabase.SaveAssets();
                    
                    string newDesc = string.IsNullOrWhiteSpace(field.value) ? "[Edit description]" : field.value;
                    descLabel.text = newDesc;
                    descLabel.style.color = new Color(1f, 1f, 1f, string.IsNullOrWhiteSpace(field.value) ? 0.4f : 1f);
                }
                
                parent.Remove(field);
                parent.Insert(index, descLabel);
            }
            
            node.mainContainer.Add(descLabel);

            var detailsContainer = new VisualElement { style = { paddingTop = 5, paddingLeft = 8, paddingRight = 8 } };
            
            var serializedQuest = new SerializedObject(m_quest);
            
            var requiredLevelField = new IntegerField("Required Level")
            {
                value = m_quest.requiredLevel,
                style = {
                    fontSize = 8,
                    marginBottom = 4
                }
            };
            requiredLevelField.Q<VisualElement>(className: "unity-base-field__input").style.width = 50;
            requiredLevelField.RegisterValueChangedCallback(evt =>
            {
                var serialized = new SerializedObject(m_quest);
                serialized.FindProperty("m_requiredLevel").intValue = evt.newValue;
                serialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            detailsContainer.Add(requiredLevelField);
            
            var recommendedLevelField = new IntegerField("Recommended Level")
            {
                value = m_quest.recommendedLevel,
                style = {
                    fontSize = 8,
                    marginBottom = 4
                }
            };
            recommendedLevelField.Q<VisualElement>(className: "unity-base-field__input").style.width = 50;
            recommendedLevelField.RegisterValueChangedCallback(evt =>
            {
                var serialized = new SerializedObject(m_quest);
                serialized.FindProperty("m_recommendedLevel").intValue = evt.newValue;
                serialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            detailsContainer.Add(recommendedLevelField);
            
            var repeatableField = new Toggle("Repeatable")
            {
                value = m_quest.repeatable,
                style = { fontSize = 8, paddingBottom = 4 }
            };
            repeatableField.RegisterValueChangedCallback(evt =>
            {
                var serialized = new SerializedObject(m_quest);
                serialized.FindProperty("m_repeatable").boolValue = evt.newValue;
                serialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            detailsContainer.Add(repeatableField);
            
            var offeredByField = new ObjectField("Offered By")
            {
                objectType = typeof(Gyvr.Mythril2D.NPCSheet),
                value = m_quest.offeredBy,
                style = { fontSize = 8, paddingRight = 8, paddingBottom = 2 }
            };
            offeredByField.RegisterValueChangedCallback(evt =>
            {
                var serialized = new SerializedObject(m_quest);
                serialized.FindProperty("m_offeredBy").objectReferenceValue = evt.newValue;
                serialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            detailsContainer.Add(offeredByField);
            
            var reportToField = new ObjectField("Report To")
            {
                objectType = typeof(Gyvr.Mythril2D.NPCSheet),
                value = m_quest.reportTo,
                style = { fontSize = 8, paddingRight = 8, paddingBottom = 2 }
            };
            reportToField.RegisterValueChangedCallback(evt =>
            {
                var serialized = new SerializedObject(m_quest);
                serialized.FindProperty("m_reportTo").objectReferenceValue = evt.newValue;
                serialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            detailsContainer.Add(reportToField);
            
            var questSerialized = new SerializedObject(m_quest);
            var completionCmdProp = questSerialized.FindProperty("m_toExecuteOnQuestCompletion");
            
            var completionCmdField = new PropertyField(completionCmdProp, "On Quest Complete");
            completionCmdField.style.fontSize = 8;
            completionCmdField.style.paddingRight = 8;
            completionCmdField.style.paddingBottom = 4;
            completionCmdField.RegisterCallback<ChangeEvent<SerializedProperty>>(evt =>
            {
                questSerialized.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            completionCmdField.Bind(questSerialized);
            detailsContainer.Add(completionCmdField);
            
            node.mainContainer.Add(detailsContainer);

            var bottomSeparator = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 6,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(bottomSeparator);
            
            var questNameLabel = new Label(m_quest.name)
            {
                name = "quest-name-label",
                style = {
                    fontSize = 7,
                    opacity = 0.5f,
                    paddingTop = 2,
                    paddingBottom = 4,
                    paddingLeft = 8,
                    minWidth = 200,
                    unityFontStyleAndWeight = FontStyle.Italic
                }
            };
            
            questNameLabel.RegisterCallback<MouseDownEvent>(evt =>
            {
                evt.StopPropagation();
                if (evt.clickCount == 1)
                {
                    var nameField = new TextField { value = m_quest.name };
                    nameField.style.fontSize = 8;
                    
                    var parent = questNameLabel.parent;
                    int index = parent.IndexOf(questNameLabel);
                    parent.Remove(questNameLabel);
                    parent.Insert(index, nameField);
                    
                    nameField.Focus();
                    nameField.SelectAll();
                    
                    nameField.RegisterCallback<BlurEvent>(blurEvt =>
                    {
                        SaveQuestNameAndRevert(nameField, parent, index, questNameLabel);
                    });
                    
                    nameField.RegisterCallback<KeyDownEvent>(keyEvt =>
                    {
                        if (keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter)
                        {
                            SaveQuestNameAndRevert(nameField, parent, index, questNameLabel);
                            keyEvt.StopPropagation();
                        }
                        else if (keyEvt.keyCode == KeyCode.Escape)
                        {
                            parent.Remove(nameField);
                            parent.Insert(index, questNameLabel);
                            keyEvt.StopPropagation();
                        }
                    });
                }
            });
            
            void SaveQuestNameAndRevert(TextField field, VisualElement parent, int index, Label label)
            {
                if (!string.IsNullOrWhiteSpace(field.value) && field.value != m_quest.name)
                {
                    string oldQuestPath = AssetDatabase.GetAssetPath(m_quest);
                    string oldQuestName = System.IO.Path.GetFileNameWithoutExtension(oldQuestPath);
                    string directory = System.IO.Path.GetDirectoryName(oldQuestPath);
                    string parentDirectory = System.IO.Path.GetDirectoryName(directory);
                    
                    string oldGraphPath = System.IO.Path.Combine(directory, oldQuestName + "_graph.asset");
                    string newQuestName = field.value;
                    string newFolderPath = System.IO.Path.Combine(parentDirectory, newQuestName);
                    
                    string renameError = AssetDatabase.RenameAsset(oldQuestPath, newQuestName);
                    if (string.IsNullOrEmpty(renameError))
                    {
                        if (System.IO.Directory.Exists(directory) && System.IO.Path.GetFileName(directory) == oldQuestName)
                        {
                            string folderRenameError = AssetDatabase.MoveAsset(directory, newFolderPath);
                            if (!string.IsNullOrEmpty(folderRenameError))
                            {
                            }
                            else
                            {
                                directory = newFolderPath;
                            }
                        }
                        
                        if (m_graphData != null)
                        {
                            string currentGraphPath = AssetDatabase.GetAssetPath(m_graphData);
                            if (!string.IsNullOrEmpty(currentGraphPath))
                            {
                                string newGraphPath = System.IO.Path.Combine(directory, newQuestName + "_graph.asset");
                                string graphRenameError = AssetDatabase.MoveAsset(currentGraphPath, newGraphPath);
                                if (string.IsNullOrEmpty(graphRenameError))
                                {
                                }
                            }
                        }
                        
                        label.text = newQuestName;
                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                    }
                    else
                    {
                    }
                }
                
                parent.Remove(field);
                parent.Insert(index, label);
            }
            
            node.mainContainer.Add(questNameLabel);

            var portContainer = new VisualElement
            {
                name = "custom-port-container",
                style =
                {
                    position = Position.Absolute,
                    right = 5,
                    top = 5,
                    flexDirection = FlexDirection.Row
                }
            };
            
            var outputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(bool));
            outputPort.portName = "";
            outputPort.portColor = new Color(1f, 0.6f, 0.2f);
            portContainer.Add(outputPort);
            
            node.Add(portContainer);

            AddElement(node);
            m_nodeViews[nodeId] = node;

            SaveNodeData(nodeId, "QuestInfo", "", position);
            
            node.RegisterCallback<GeometryChangedEvent>(evt =>
            {
                if (m_graphData != null)
                {
                    var nodeData = m_graphData.nodes.FirstOrDefault(n => n.nodeId == nodeId);
                    if (nodeData != null)
                    {
                        var newPos = node.GetPosition().position;
                        if (Vector2.Distance(nodeData.position, newPos) > 1f)
                        {
                            nodeData.position = newPos;
                            EditorUtility.SetDirty(m_graphData);
                        }
                    }
                }
            });

            return node;
        }

        private Node CreateTaskNode(int taskIndex, Vector2 position, string nodeId = null)
        {
            if (m_quest.tasks == null || taskIndex >= m_quest.tasks.Length)
            {
                return null;
            }

            var task = m_quest.tasks[taskIndex];
            
            if (nodeId == null)
            {
                nodeId = $"task-{taskIndex}";
            }

            string nodeTitle = GetTaskNodeTitle(task, taskIndex);
            bool isUnconfigured = nodeTitle.Contains("(unconfigured)");
            
            if (isUnconfigured)
            {
                nodeTitle = nodeTitle.Replace(" (unconfigured)", "").Replace("(unconfigured)", "");
            }
            
            var node = new Node
            {
                title = nodeTitle,
                name = nodeId
            };
            
            node.capabilities &= ~Capabilities.Collapsible;
            
            var titleButtonsContainer = node.Q("title-button-container");
            if (titleButtonsContainer != null)
                titleButtonsContainer.style.display = DisplayStyle.None;
            node.userData = new System.Tuple<QuestTask, int>(task, taskIndex);
            
            if (isUnconfigured)
            {
                var warningIcon = new Label("⚠")
                {
                    style = {
                        position = Position.Absolute,
                        right = 8,
                        top = 4,
                        fontSize = 14,
                        unityTextAlign = TextAnchor.MiddleRight
                    }
                };
                warningIcon.tooltip = "Task is not fully configured";
                node.titleContainer.Add(warningIcon);
            }
            
            node.RegisterCallback<MouseDownEvent>(evt =>
            {
                if (evt.button == 0)
                {
                    Selection.activeObject = task;
                }
            });

            node.SetPosition(new Rect(position, Vector2.zero));
            
            node.mainContainer.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 1f);
            node.mainContainer.style.maxWidth = 250;
            node.mainContainer.style.minWidth = 250;
            
            node.style.borderBottomWidth = 0;
            node.style.borderLeftWidth = 0;
            node.style.borderRightWidth = 0;
            node.style.borderTopWidth = 0;
            node.style.paddingBottom = 0;
            node.style.paddingLeft = 0;
            node.style.paddingRight = 0;
            node.style.paddingTop = 0;
            node.style.marginTop = 1;
            node.style.marginBottom = 1;
            node.style.marginLeft = 1;
            node.style.marginRight = 1;
            node.style.overflow = Overflow.Hidden;
            
            node.titleContainer.style.backgroundColor = new Color(0.6f, 0.4f, 0.8f, 0.4f);
            node.titleContainer.style.paddingLeft = 18;
            
            var taskTitleLabel = node.titleContainer.Q<Label>();
            if (taskTitleLabel != null && EditorFont != null)
            {
                taskTitleLabel.style.unityFont = EditorFont;
            }

            var serializedTask = new SerializedObject(task);
            string taskType = task.GetType().Name;
            
            AddTaskField(node, serializedTask, "m_title", "Title Format:");
            
            if (taskType == "ItemTask")
            {
                AddTaskField(node, serializedTask, "item", "Item:");
                AddTaskField(node, serializedTask, "amountToCollect", "Amount:");
            }
            else if (taskType == "KillMonsterTask")
            {
                AddTaskField(node, serializedTask, "monster", "Monster:");
                AddTaskField(node, serializedTask, "monstersToKill", "Count:");
            }
            else if (taskType == "TalkToNPCTask")
            {
                AddTaskField(node, serializedTask, "target", "NPC:");
                AddNPCPickerButton(node, serializedTask, task as TalkToNPCTask);
            }
            else if (taskType == "GameFlagTask")
            {
                AddTaskField(node, serializedTask, "gameFlags", "Flags:");
            }
            
            if (taskIndex > 0)
            {
                var requirePrevProp = serializedTask.FindProperty("requirePreviousTaskCompletion");
                if (requirePrevProp != null)
                {
                    AddTaskField(node, serializedTask, "requirePreviousTaskCompletion", "Require Previous:");
                }
            }
            
            var bottomSeparator = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 6,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(bottomSeparator);
            
            var nameLabel = new Label(task.name)
            {
                name = "task-name-label",
                style = {
                    fontSize = 7,
                    opacity = 0.5f,
                    paddingTop = 2,
                    paddingBottom = 4,
                    paddingLeft = 8,
                    minWidth = 200,
                    unityFontStyleAndWeight = FontStyle.Italic
                }
            };
            
            nameLabel.RegisterCallback<MouseDownEvent>(evt =>
            {
                evt.StopPropagation();
                if (evt.clickCount == 1)
                {
                    var nameField = new TextField { value = task.name };
                    nameField.style.fontSize = 8;
                    
                    var parent = nameLabel.parent;
                    int index = parent.IndexOf(nameLabel);
                    parent.Remove(nameLabel);
                    parent.Insert(index, nameField);
                    
                    nameField.Focus();
                    nameField.SelectAll();
                    
                    nameField.RegisterCallback<BlurEvent>(blurEvt =>
                    {
                        SaveTaskNameAndRevert(nameField, parent, index, nameLabel, task);
                    });
                    
                    nameField.RegisterCallback<KeyDownEvent>(keyEvt =>
                    {
                        if (keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter)
                        {
                            SaveTaskNameAndRevert(nameField, parent, index, nameLabel, task);
                            keyEvt.StopPropagation();
                        }
                        else if (keyEvt.keyCode == KeyCode.Escape)
                        {
                            parent.Remove(nameField);
                            parent.Insert(index, nameLabel);
                            keyEvt.StopPropagation();
                        }
                    });
                }
            });
            
            void SaveTaskNameAndRevert(TextField field, VisualElement parent, int index, Label label, QuestTask taskToRename)
            {
                if (!string.IsNullOrWhiteSpace(field.value) && field.value != taskToRename.name)
                {
                    string oldPath = AssetDatabase.GetAssetPath(taskToRename);
                    string directory = System.IO.Path.GetDirectoryName(oldPath);
                    string extension = System.IO.Path.GetExtension(oldPath);
                    string newPath = System.IO.Path.Combine(directory, field.value + extension);
                    
                    string error = AssetDatabase.RenameAsset(oldPath, field.value);
                    if (string.IsNullOrEmpty(error))
                    {
                        label.text = field.value;
                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                    }
                    else
                    {
                    }
                }
                
                parent.Remove(field);
                parent.Insert(index, label);
            }
            
            node.mainContainer.Add(nameLabel);
            
            node.inputContainer.style.display = DisplayStyle.None;
            node.outputContainer.style.display = DisplayStyle.None;
            
            var inputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, typeof(bool));
            inputPort.portName = "";
            inputPort.portColor = new Color(0.6f, 0.4f, 0.8f);
            inputPort.style.position = Position.Absolute;
            inputPort.style.left = 2;
            inputPort.style.top = 10;
            node.Add(inputPort);
            
            var outputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(bool));
            outputPort.portName = "";
            outputPort.portColor = new Color(0.6f, 0.4f, 0.8f);
            outputPort.style.position = Position.Absolute;
            outputPort.style.right = 2;
            outputPort.style.top = 10;
            node.Add(outputPort);
            
            var hintOverrideBar = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    alignItems = Align.Center,
                    justifyContent = Justify.FlexEnd,
                    backgroundColor = new Color(0.9f, 0.6f, 0.2f, 0.15f),
                    paddingRight = 0,
                    paddingLeft = 8,
                    paddingTop = 0,
                    paddingBottom = 0,
                    marginTop = 2,
                    marginRight = 0,
                    position = Position.Relative,
                    height = 22
                }
            };
            
            var hintPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(bool));
            hintPort.portName = "Hint Override";
            hintPort.portColor = new Color(0.9f, 0.6f, 0.2f);
            hintPort.style.marginRight = 0;
            hintOverrideBar.Add(hintPort);
            
            node.mainContainer.Add(hintOverrideBar);

            AddElement(node);
            m_nodeViews[nodeId] = node;

            SaveNodeData(nodeId, "Task", taskIndex.ToString(), position);
            
            node.RegisterCallback<GeometryChangedEvent>(evt =>
            {
                if (m_graphData != null)
                {
                    var nodeData = m_graphData.nodes.FirstOrDefault(n => n.nodeId == nodeId);
                    if (nodeData != null)
                    {
                        var newPos = node.GetPosition().position;
                        if (Vector2.Distance(nodeData.position, newPos) > 1f)
                        {
                            nodeData.position = newPos;
                            EditorUtility.SetDirty(m_graphData);
                        }
                    }
                }
            });

            return node;
        }

        private Node CreateDialogueNode(Gyvr.Mythril2D.DialogueSequence dialogue, string label, Vector2 position, string nodeId = null, string role = "")
        {
            if (dialogue == null)
            {
                return null;
            }
            
            if (nodeId == null)
            {
                nodeId = $"dialogue-{dialogue.GetInstanceID()}";
            }
            
            bool isDefaultHint = (role == "QuestHint");
            
            var node = new Node
            {
                title = label,
                name = nodeId
            };
            
            string cleanLabel = label.Replace("⚠ ", "");
            if (dialogue.lines == null || dialogue.lines.Length == 0)
            {
                node.title = "⚠ " + cleanLabel;
            }
            else
            {
                node.title = cleanLabel;
            }
            
            node.capabilities &= ~Capabilities.Collapsible;
            
            if (role == "QuestOffer" || role == "QuestHint" || role == "QuestCompleted")
            {
                node.capabilities &= ~Capabilities.Deletable;
            }
            
            var titleButtonsContainer = node.Q("title-button-container");
            if (titleButtonsContainer != null)
                titleButtonsContainer.style.display = DisplayStyle.None;
            node.userData = dialogue;
            
            node.RegisterCallback<MouseDownEvent>(evt =>
            {
                if (evt.button == 0)
                {
                    Selection.activeObject = dialogue;
                }
            });
            
            node.SetPosition(new Rect(position, Vector2.zero));
            
            node.mainContainer.style.backgroundColor = new Color(0.18f, 0.18f, 0.18f, 1f);
            node.mainContainer.style.maxWidth = 280;
            node.mainContainer.style.width = 280;
            node.mainContainer.style.overflow = Overflow.Hidden;
            
            node.style.borderBottomWidth = 0;
            node.style.borderLeftWidth = 0;
            node.style.borderRightWidth = 0;
            node.style.borderTopWidth = 0;
            node.style.paddingBottom = 0;
            node.style.paddingLeft = 0;
            node.style.paddingRight = 0;
            node.style.paddingTop = 0;
            node.style.marginTop = 1;
            node.style.marginBottom = 1;
            node.style.marginLeft = 1;
            node.style.marginRight = 1;
            
            node.style.maxWidth = 280;
            node.style.minWidth = 280;
            node.style.width = 280;
            
            if (label.Contains("Completed"))
            {
                node.titleContainer.style.backgroundColor = new Color(0.2f, 0.6f, 0.3f, 0.5f);
            }
            else if (label.Contains("Offer"))
            {
                node.titleContainer.style.backgroundColor = new Color(0.5f, 0.7f, 1f, 0.4f);
            }
            else if (label.Contains("Hint"))
            {
                node.titleContainer.style.backgroundColor = new Color(0.9f, 0.6f, 0.2f, 0.4f);
            }
            
            node.titleContainer.style.paddingLeft = isDefaultHint ? 8 : 18;
            node.titleContainer.style.paddingRight = 8;
            node.titleContainer.style.borderTopLeftRadius = 6;
            node.titleContainer.style.borderTopRightRadius = 6;
            
            var dialogueTitleLabel = node.titleContainer.Q<Label>();
            if (dialogueTitleLabel != null)
            {
                if (EditorFont != null)
                {
                    dialogueTitleLabel.style.unityFont = EditorFont;
                }
                dialogueTitleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
            }
            
            int selectedLineIndex = -1;
            int selectedOptionIndex = -1;
            
            var linesHeader = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    paddingLeft = 8,
                    paddingRight = 16,
                    paddingTop = 4,
                    paddingBottom = 2,
                    justifyContent = Justify.SpaceBetween,
                    alignItems = Align.Center
                }
            };
            
            var linesLabel = new Label("Lines")
            {
                style = {
                    fontSize = 9,
                    opacity = 0.7f,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            linesHeader.Add(linesLabel);
            
            var lineButtonsContainer = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    marginRight = 0
                }
            };
            
            var addLineButton = new Button(() =>
            {
                var serializedDialogue = new SerializedObject(dialogue);
                var linesProperty = serializedDialogue.FindProperty("lines");
                linesProperty.InsertArrayElementAtIndex(linesProperty.arraySize);
                linesProperty.GetArrayElementAtIndex(linesProperty.arraySize - 1).stringValue = "New line";
                serializedDialogue.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
                RefreshDialogueNode(node, dialogue);
            })
            {
                text = "+",
                style = {
                    fontSize = 10,
                    paddingTop = 2,
                    paddingBottom = 2,
                    paddingLeft = 8,
                    paddingRight = 8,
                    marginRight = 2,
                    backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f),
                    borderTopWidth = 1,
                    borderBottomWidth = 1,
                    borderLeftWidth = 1,
                    borderRightWidth = 1,
                    borderTopColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderBottomColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderLeftColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderRightColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderTopLeftRadius = 3,
                    borderTopRightRadius = 3,
                    borderBottomLeftRadius = 3,
                    borderBottomRightRadius = 3,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            lineButtonsContainer.Add(addLineButton);
            
            var removeLineButton = new Button(() =>
            {
                if (selectedLineIndex >= 0)
                {
                    var serializedDialogue = new SerializedObject(dialogue);
                    var linesProperty = serializedDialogue.FindProperty("lines");
                    linesProperty.DeleteArrayElementAtIndex(selectedLineIndex);
                    serializedDialogue.ApplyModifiedProperties();
                    AssetDatabase.SaveAssets();
                    selectedLineIndex = -1;
                    RefreshDialogueNode(node, dialogue);
                }
            })
            {
                text = "-",
                style = {
                    fontSize = 10,
                    paddingTop = 2,
                    paddingBottom = 2,
                    paddingLeft = 8,
                    paddingRight = 8,
                    backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f),
                    borderTopWidth = 1,
                    borderBottomWidth = 1,
                    borderLeftWidth = 1,
                    borderRightWidth = 1,
                    borderTopColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderBottomColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderLeftColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderRightColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderTopLeftRadius = 3,
                    borderTopRightRadius = 3,
                    borderBottomLeftRadius = 3,
                    borderBottomRightRadius = 3,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            lineButtonsContainer.Add(removeLineButton);
            
            linesHeader.Add(lineButtonsContainer);
            node.mainContainer.Add(linesHeader);
            
            if (dialogue.lines != null && dialogue.lines.Length > 0)
            {
                for (int i = 0; i < dialogue.lines.Length; i++)
                {
                    int lineIndex = i;
                    string lineText = dialogue.lines[i];
                    
                    var lineContainer = new VisualElement
                    {
                        style = {
                            flexDirection = FlexDirection.Row,
                            alignItems = Align.Center,
                            backgroundColor = new Color(0, 0, 0, 0),
                            maxWidth = 300,
                            minWidth = 230
                        }
                    };
                    
                    var lineLabel = new Label($"{i + 1}. {lineText}")
                    {
                        name = $"line-{i}",
                        style = { 
                            fontSize = 11, 
                            opacity = 1f, 
                            paddingBottom = 5,
                            paddingTop = 2,
                            paddingLeft = 8,
                            paddingRight = 8,
                            whiteSpace = WhiteSpace.Normal,
                            maxWidth = 280,
                            width = 280,
                            flexShrink = 0
                        }
                    };
                    
                    lineContainer.Add(lineLabel);
                    
                    lineLabel.RegisterCallback<MouseDownEvent>(evt =>
                    {
                        if (evt.clickCount == 2)
                        {
                            var lineField = new TextField { value = dialogue.lines[lineIndex], multiline = true };
                            lineField.style.fontSize = 8;
                            lineField.style.minHeight = 40;
                            lineField.style.maxWidth = 280;
                            lineField.style.width = 280;
                            lineField.isDelayed = false;
                            
                            var parent = lineLabel.parent;
                            int idx = parent.IndexOf(lineLabel);
                            parent.Remove(lineLabel);
                            parent.Insert(idx, lineField);
                            
                            lineField.Focus();
                            lineField.SelectAll();
                            
                            lineField.RegisterCallback<BlurEvent>(blurEvt =>
                            {
                                SaveLineAndRevert(lineField, parent, idx, dialogue, lineIndex, lineLabel);
                            });
                            
                            lineField.RegisterCallback<KeyDownEvent>(keyEvt =>
                            {
                                if ((keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter) && keyEvt.ctrlKey)
                                {
                                    SaveLineAndRevert(lineField, parent, idx, dialogue, lineIndex, lineLabel);
                                    keyEvt.StopPropagation();
                                }
                                else if (keyEvt.keyCode == KeyCode.Escape)
                                {
                                    parent.Remove(lineField);
                                    parent.Insert(idx, lineLabel);
                                    keyEvt.StopPropagation();
                                }
                            });
                            
                            evt.StopPropagation();
                        }
                    });
                    
                    lineContainer.RegisterCallback<MouseDownEvent>(evt =>
                    {
                        if (evt.clickCount == 1)
                        {
                            if (selectedLineIndex == lineIndex)
                            {
                                selectedLineIndex = -1;
                                lineContainer.style.backgroundColor = new Color(0, 0, 0, 0);
                                removeLineButton.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f);
                            }
                            else
                            {
                                selectedLineIndex = lineIndex;
                                lineContainer.style.backgroundColor = new Color(0.5f, 0.3f, 0.3f, 0.3f);
                                removeLineButton.style.backgroundColor = new Color(0.6f, 0.2f, 0.2f, 1f);
                            }
                            evt.StopPropagation();
                        }
                    });
                    
                    void SaveLineAndRevert(TextField field, VisualElement parent, int idx, Gyvr.Mythril2D.DialogueSequence dlg, int lIdx, Label lbl)
                    {
                        if (field.value != dlg.lines[lIdx])
                        {
                            var serializedDialogue = new SerializedObject(dlg);
                            var linesProperty = serializedDialogue.FindProperty("lines");
                            linesProperty.GetArrayElementAtIndex(lIdx).stringValue = field.value;
                            serializedDialogue.ApplyModifiedProperties();
                            AssetDatabase.SaveAssets();
                            
                            lbl.text = $"{lIdx + 1}. {field.value}";
                        }
                        parent.Remove(field);
                        parent.Insert(idx, lbl);
                    }
                    
                    node.mainContainer.Add(lineContainer);
                }
            }
            else
            {
                var noLinesLabel = new Label("(No dialogue lines)")
                {
                    style = { 
                        fontSize = 11, 
                        opacity = 0.4f, 
                        paddingLeft = 8,
                        paddingTop = 5,
                        paddingBottom = 8,
                        unityFontStyleAndWeight = FontStyle.Italic 
                    }
                };
                node.mainContainer.Add(noLinesLabel);
            }
            
            var optionsHeader = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    paddingLeft = 8,
                    paddingRight = 16,
                    paddingTop = 8,
                    paddingBottom = 2,
                    justifyContent = Justify.SpaceBetween,
                    alignItems = Align.Center
                }
            };
            
            var optionsLabel = new Label("Options")
            {
                style = {
                    fontSize = 9,
                    opacity = 0.7f,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            optionsHeader.Add(optionsLabel);
            
            var optionButtonsContainer = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    marginRight = 0
                }
            };
            
            var addOptionButton = new Button(() =>
            {
                var serializedDialogue = new SerializedObject(dialogue);
                var optionsProperty = serializedDialogue.FindProperty("options");
                optionsProperty.InsertArrayElementAtIndex(optionsProperty.arraySize);
                var newOption = optionsProperty.GetArrayElementAtIndex(optionsProperty.arraySize - 1);
                newOption.FindPropertyRelative("name").stringValue = "New option";
                serializedDialogue.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
                RefreshDialogueNode(node, dialogue);
            })
            {
                text = "+",
                style = {
                    fontSize = 10,
                    paddingTop = 2,
                    paddingBottom = 2,
                    paddingLeft = 8,
                    paddingRight = 8,
                    marginRight = 2,
                    backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f),
                    borderTopWidth = 1,
                    borderBottomWidth = 1,
                    borderLeftWidth = 1,
                    borderRightWidth = 1,
                    borderTopColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderBottomColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderLeftColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderRightColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderTopLeftRadius = 3,
                    borderTopRightRadius = 3,
                    borderBottomLeftRadius = 3,
                    borderBottomRightRadius = 3,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            optionButtonsContainer.Add(addOptionButton);
            
            var removeOptionButton = new Button(() =>
            {
                if (selectedOptionIndex >= 0)
                {
                    string optionPortName = $"option-{selectedOptionIndex}";
                    var edgesToRemove = edges.ToList().Where(e => 
                        e.output?.node == node && e.output?.portName == optionPortName).ToList();
                    
                    foreach (var edge in edgesToRemove)
                    {
                        if (edge.input?.node is Node targetNode)
                        {
                            if (targetNode.userData is DialogueSequence)
                            {
                                var targetNodeData = m_graphData?.GetNode(targetNode.name);
                                if (targetNodeData != null && targetNodeData.role != "QuestOffer" && 
                                    targetNodeData.role != "QuestHint" && targetNodeData.role != "QuestCompleted")
                                {
                                    targetNode.title = "Dialogue";
                                    SaveNodeCustomTitle(targetNode.name, "");
                                }
                            }
                            
                            m_graphData?.RemoveConnection(node.name, targetNode.name);
                        }
                        RemoveElement(edge);
                    }
                    
                    if (m_graphData != null)
                    {
                        var connectionsToUpdate = m_graphData.connections
                            .Where(c => c.fromNodeId == node.name && c.fromPortName.StartsWith("option-"))
                            .ToList();
                        
                        foreach (var conn in connectionsToUpdate)
                        {
                            if (int.TryParse(conn.fromPortName.Replace("option-", ""), out int portIndex))
                            {
                                if (portIndex > selectedOptionIndex)
                                {
                                    conn.fromPortName = $"option-{portIndex - 1}";
                                }
                            }
                        }
                        EditorUtility.SetDirty(m_graphData);
                    }
                    
                    var serializedDialogue = new SerializedObject(dialogue);
                    var optionsProperty = serializedDialogue.FindProperty("options");
                    optionsProperty.DeleteArrayElementAtIndex(selectedOptionIndex);
                    serializedDialogue.ApplyModifiedProperties();
                    AssetDatabase.SaveAssets();
                    selectedOptionIndex = -1;
                    RefreshDialogueNode(node, dialogue);
                }
            })
            {
                text = "-",
                style = {
                    fontSize = 10,
                    paddingTop = 2,
                    paddingBottom = 2,
                    paddingLeft = 8,
                    paddingRight = 8,
                    backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f),
                    borderTopWidth = 1,
                    borderBottomWidth = 1,
                    borderLeftWidth = 1,
                    borderRightWidth = 1,
                    borderTopColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderBottomColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderLeftColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderRightColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderTopLeftRadius = 3,
                    borderTopRightRadius = 3,
                    borderBottomLeftRadius = 3,
                    borderBottomRightRadius = 3,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            optionButtonsContainer.Add(removeOptionButton);
            
            optionsHeader.Add(optionButtonsContainer);
            
            if (!isDefaultHint)
            {
                node.mainContainer.Add(optionsHeader);
            }
            
            if (dialogue.options != null && dialogue.options.Length > 0)
            {
                
                for (int i = 0; i < dialogue.options.Length; i++)
                {
                    int optionIndex = i;
                    var option = dialogue.options[i];
                    string optionName = string.IsNullOrEmpty(option.name) ? $"Option {i + 1}" : option.name;
                    
                    var optionContainer = new VisualElement
                    {
                        style = {
                            flexDirection = FlexDirection.Row,
                            backgroundColor = new Color(0, 0, 0, 0),
                            maxWidth = 270,
                            minWidth = 230,
                            paddingBottom = 5,
                            paddingLeft = 8,
                            paddingRight = 8,
                            alignItems = Align.Center
                        }
                    };
                    
                    var optionLabel = new Label($"{i + 1}. {optionName}")
                    {
                        name = $"option-{i}",
                        style = { 
                            fontSize = 11, 
                            opacity = 1f, 
                            paddingBottom = 2,
                            paddingTop = 2,
                            paddingRight = 8,
                            whiteSpace = WhiteSpace.Normal,
                            flexGrow = 1,
                            flexShrink = 1
                        }
                    };
                    
                    optionContainer.Add(optionLabel);
                    
                    var customMessageField = new TextField
                    {
                        value = option.message.customMessage ?? "",
                        style = {
                            fontSize = 9,
                            minWidth = 80,
                            maxWidth = 100,
                            marginRight = 5,
                            display = option.message.type == Gyvr.Mythril2D.EDialogueMessageType.Custom ? DisplayStyle.Flex : DisplayStyle.None
                        }
                    };
                    customMessageField.RegisterValueChangedCallback(evt =>
                    {
                        var serializedDialogue = new SerializedObject(dialogue);
                        var optionsProperty = serializedDialogue.FindProperty("options");
                        var optionProperty = optionsProperty.GetArrayElementAtIndex(optionIndex);
                        var messageProperty = optionProperty.FindPropertyRelative("message");
                        var customProperty = messageProperty.FindPropertyRelative("customMessage");
                        
                        customProperty.stringValue = evt.newValue;
                        serializedDialogue.ApplyModifiedProperties();
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                        
                        var activeObject = Selection.activeObject;
                        Selection.activeObject = null;
                        Selection.activeObject = activeObject;
                    });
                    optionContainer.Add(customMessageField);
                    
                    var messageTypeChoices = new List<string> { "None", "Accept", "Decline", "Custom" };
                    var currentMessageType = option.message.type.ToString();
                    int selectedMessageIndex = messageTypeChoices.IndexOf(currentMessageType);
                    if (selectedMessageIndex < 0) selectedMessageIndex = 0;
                    
                    var messageTypeDropdown = new PopupField<string>(messageTypeChoices, selectedMessageIndex)
                    {
                        style = {
                            fontSize = 9,
                            maxWidth = 70,
                            minWidth = 70
                        }
                    };
                    
                    messageTypeDropdown.RegisterValueChangedCallback(evt =>
                    {
                        
                        var messageType = (Gyvr.Mythril2D.EDialogueMessageType)System.Enum.Parse(typeof(Gyvr.Mythril2D.EDialogueMessageType), evt.newValue);
                        
                        dialogue.options[optionIndex].message.type = messageType;
                        
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                        
                        if (evt.newValue == "Custom")
                        {
                            customMessageField.style.display = DisplayStyle.Flex;
                        }
                        else
                        {
                            customMessageField.style.display = DisplayStyle.None;
                        }
                        
                        EditorApplication.delayCall += () =>
                        {
                            var tempSO = new SerializedObject(dialogue);
                            tempSO.Update();
                            
                            var activeObject = Selection.activeObject;
                            if (activeObject == dialogue)
                            {
                                Selection.activeObject = null;
                                EditorApplication.delayCall += () =>
                                {
                                    Selection.activeObject = dialogue;
                                };
                            }
                        };
                    });
                    
                    optionContainer.Add(messageTypeDropdown);
                    
                    if (!isDefaultHint)
                    {
                        var optionPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(bool));
                        optionPort.portName = $"option-{i}";
                        optionPort.portColor = new Color(1f, 0.6f, 0.2f);
                        optionPort.style.position = Position.Absolute;
                        optionPort.style.right = -10;
                        optionPort.style.top = StyleKeyword.Auto;
                        optionPort.style.bottom = StyleKeyword.Auto;
                        optionContainer.style.position = Position.Relative;
                        
                        var portLabel = optionPort.Q<Label>();
                        if (portLabel != null)
                        {
                            portLabel.style.display = DisplayStyle.None;
                        }
                        
                        optionContainer.Add(optionPort);
                    }
                    
                    optionLabel.RegisterCallback<MouseDownEvent>(evt =>
                    {
                        if (evt.clickCount == 2)
                        {
                            var optionField = new TextField { value = option.name };
                            optionField.style.fontSize = 8;
                            optionField.style.maxWidth = 280;
                            optionField.style.width = 280;
                            optionField.isDelayed = false;
                            
                            var parent = optionLabel.parent;
                            int idx = parent.IndexOf(optionLabel);
                            parent.Remove(optionLabel);
                            parent.Insert(idx, optionField);
                            
                            optionField.Focus();
                            optionField.SelectAll();
                            
                            optionField.RegisterCallback<BlurEvent>(blurEvt =>
                            {
                                SaveOptionAndRevert(optionField, parent, idx, dialogue, optionIndex, optionLabel);
                            });
                            
                            optionField.RegisterCallback<KeyDownEvent>(keyEvt =>
                            {
                                if (keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter)
                                {
                                    SaveOptionAndRevert(optionField, parent, idx, dialogue, optionIndex, optionLabel);
                                    keyEvt.StopPropagation();
                                }
                                else if (keyEvt.keyCode == KeyCode.Escape)
                                {
                                    parent.Remove(optionField);
                                    parent.Insert(idx, optionLabel);
                                    keyEvt.StopPropagation();
                                }
                            });
                            
                            evt.StopPropagation();
                        }
                    });
                    
                    optionContainer.RegisterCallback<MouseDownEvent>(evt =>
                    {
                        if (evt.clickCount == 1)
                        {
                            if (selectedOptionIndex == optionIndex)
                            {
                                selectedOptionIndex = -1;
                                optionContainer.style.backgroundColor = new Color(0, 0, 0, 0);
                                removeOptionButton.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 1f);
                            }
                            else
                            {
                                selectedOptionIndex = optionIndex;
                                optionContainer.style.backgroundColor = new Color(0.5f, 0.3f, 0.3f, 0.3f);
                                removeOptionButton.style.backgroundColor = new Color(0.6f, 0.2f, 0.2f, 1f);
                            }
                            evt.StopPropagation();
                        }
                    });
                    
                    void SaveOptionAndRevert(TextField field, VisualElement parent, int idx, Gyvr.Mythril2D.DialogueSequence dlg, int optIdx, Label lbl)
                    {
                        if (field.value != dlg.options[optIdx].name)
                        {
                            var serializedDialogue = new SerializedObject(dlg);
                            var optionsProperty = serializedDialogue.FindProperty("options");
                            optionsProperty.GetArrayElementAtIndex(optIdx).FindPropertyRelative("name").stringValue = field.value;
                            serializedDialogue.ApplyModifiedProperties();
                            EditorUtility.SetDirty(dlg);
                            AssetDatabase.SaveAssets();
                            
                            var activeObject = Selection.activeObject;
                            Selection.activeObject = null;
                            Selection.activeObject = activeObject;
                            
                            lbl.text = $"{optIdx + 1}. {field.value}";
                            
                            string optionPortName = $"option-{optIdx}";
                            var connectedEdge = edges.ToList().FirstOrDefault(e => 
                                e.output?.node == node && e.output?.portName == optionPortName);
                            
                            if (connectedEdge != null && connectedEdge.input?.node is Node connectedNode)
                            {
                                connectedNode.title = $"\"{field.value}\"";
                                SaveNodeCustomTitle(connectedNode.name, connectedNode.title);
                            }
                            else if (dlg.options[optIdx].sequence != null)
                            {
                                var connectedDialogue = dlg.options[optIdx].sequence;
                                var seqConnectedNode = nodes.ToList().FirstOrDefault(n => n.userData is UnityEngine.Object obj && obj == connectedDialogue);
                                if (seqConnectedNode != null)
                                {
                                    seqConnectedNode.title = $"\"{field.value}\"";
                                    SaveNodeCustomTitle(seqConnectedNode.name, seqConnectedNode.title);
                                }
                            }
                        }
                        parent.Remove(field);
                        parent.Insert(idx, lbl);
                    }
                    
                    node.mainContainer.Add(optionContainer);
                }
            }
            
            var commandsHeader = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    justifyContent = Justify.SpaceBetween,
                    alignItems = Align.Center,
                    paddingLeft = 8,
                    paddingRight = 16,
                    paddingTop = 6,
                    paddingBottom = 2
                }
            };
            
            var commandsLabel = new Label("Commands:")
            {
                style = {
                    fontSize = 9,
                    opacity = 0.7f,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            commandsHeader.Add(commandsLabel);
            
            var nodeData = m_graphData?.GetNode(nodeId);
            bool showCommands = false;
            if (nodeData?.metadata != null)
            {
                var showCommandsEntry = nodeData.metadata.Find(e => e.key == "showCommands");
                showCommands = showCommandsEntry != null ? bool.Parse(showCommandsEntry.value) : (dialogue.toExecuteOnStart != null || dialogue.toExecuteOnCompletion != null);
            }
            else
            {
                showCommands = dialogue.toExecuteOnStart != null || dialogue.toExecuteOnCompletion != null;
            }
            
            var commandsToggle = new Button(() =>
            {
                showCommands = !showCommands;
                
                if (m_graphData != null)
                {
                    var data = m_graphData.GetNode(nodeId);
                    if (data != null)
                    {
                        if (data.metadata == null)
                            data.metadata = new System.Collections.Generic.List<MetadataEntry>();
                        
                        var entry = data.metadata.Find(e => e.key == "showCommands");
                        if (entry != null)
                            entry.value = showCommands.ToString();
                        else
                            data.metadata.Add(new MetadataEntry { key = "showCommands", value = showCommands.ToString() });
                        
                        EditorUtility.SetDirty(m_graphData);
                    }
                }
                
                if (!showCommands)
                {
                    var serializedDialogue = new SerializedObject(dialogue);
                    serializedDialogue.FindProperty("toExecuteOnStart").managedReferenceValue = null;
                    serializedDialogue.FindProperty("toExecuteOnCompletion").managedReferenceValue = null;
                    serializedDialogue.ApplyModifiedProperties();
                    AssetDatabase.SaveAssets();
                }
                
                RefreshDialogueNode(node, dialogue);
            })
            {
                text = showCommands ? "✓" : "○",
                style = {
                    fontSize = 10,
                    paddingTop = 2,
                    paddingBottom = 2,
                    paddingLeft = 8,
                    paddingRight = 8,
                    backgroundColor = showCommands ? new Color(0.2f, 0.5f, 0.2f, 0.8f) : new Color(0.15f, 0.15f, 0.15f, 1f),
                    borderTopWidth = 1,
                    borderBottomWidth = 1,
                    borderLeftWidth = 1,
                    borderRightWidth = 1,
                    borderTopColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderBottomColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderLeftColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderRightColor = new Color(0.3f, 0.3f, 0.3f, 1f),
                    borderTopLeftRadius = 3,
                    borderTopRightRadius = 3,
                    borderBottomLeftRadius = 3,
                    borderBottomRightRadius = 3,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            commandsHeader.Add(commandsToggle);
            
            node.mainContainer.Add(commandsHeader);
            
            if (showCommands)
            {
                var serializedDialogue = new SerializedObject(dialogue);
                var onStartProp = serializedDialogue.FindProperty("toExecuteOnStart");
                
                var onStartLabel = new Label("On Start:")
                {
                    style = {
                        fontSize = 7,
                        opacity = 0.6f,
                        paddingLeft = 12,
                        paddingTop = 3,
                        paddingBottom = 1,
                        unityFontStyleAndWeight = FontStyle.Bold
                    }
                };
                node.mainContainer.Add(onStartLabel);
                
                var onStartContainer = new VisualElement();
                
                void RebuildOnStartField()
                {
                    onStartContainer.Clear();
                    serializedDialogue.Update();
                    
                    onStartProp.isExpanded = true;
                    
                    var onStartField = new PropertyField(onStartProp, "");
                    onStartField.SetEnabled(true);
                    onStartField.style.fontSize = 7;
                    onStartField.style.paddingLeft = 8;
                    onStartField.style.paddingRight = 8;
                    onStartField.style.paddingTop = 2;
                    onStartField.style.paddingBottom = 2;
                    onStartField.style.minWidth = 300;
                    onStartField.style.maxWidth = 340;
                    onStartField.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 0.5f);
                    onStartField.style.borderTopLeftRadius = 3;
                    onStartField.style.borderTopRightRadius = 3;
                    onStartField.style.borderBottomLeftRadius = 3;
                    onStartField.style.borderBottomRightRadius = 3;
                    
                    onStartField.Bind(serializedDialogue);
                    
                    onStartField.TrackPropertyValue(onStartProp, prop => 
                    {
                        serializedDialogue.ApplyModifiedProperties();
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                        EditorApplication.delayCall += RebuildOnStartField;
                    });
                    
                    onStartField.RegisterCallback<SerializedPropertyChangeEvent>(evt =>
                    {
                        serializedDialogue.ApplyModifiedProperties();
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                    });
                    
                    onStartContainer.Add(onStartField);
                    
                    EditorApplication.delayCall += () =>
                    {
                        EditorApplication.delayCall += () =>
                        {
                            
                            var foldout = onStartField.Q<Foldout>();
                            if (foldout != null)
                            {
                                foldout.value = true;
                            }
                            else
                            {
                                foreach (var child in onStartField.Children())
                                {
                                }
                            }
                        };
                    };
                }
                
                RebuildOnStartField();
                node.mainContainer.Add(onStartContainer);
                
                var onCompletionProp = serializedDialogue.FindProperty("toExecuteOnCompletion");
                
                var onCompleteLabel = new Label("On Complete:")
                {
                    style = {
                        fontSize = 7,
                        opacity = 0.6f,
                        paddingLeft = 12,
                        paddingTop = 3,
                        paddingBottom = 1,
                        unityFontStyleAndWeight = FontStyle.Bold
                    }
                };
                node.mainContainer.Add(onCompleteLabel);
                
                var onCompletionContainer = new VisualElement();
                
                void RebuildOnCompletionField()
                {
                    onCompletionContainer.Clear();
                    serializedDialogue.Update();
                    
                    onCompletionProp.isExpanded = true;
                    
                    var onCompletionField = new PropertyField(onCompletionProp, "");
                    onCompletionField.SetEnabled(true);
                    onCompletionField.style.fontSize = 7;
                    onCompletionField.style.paddingLeft = 8;
                    onCompletionField.style.paddingRight = 8;
                    onCompletionField.style.paddingTop = 2;
                    onCompletionField.style.paddingBottom = 2;
                    onCompletionField.style.minWidth = 300;
                    onCompletionField.style.maxWidth = 340;
                    onCompletionField.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 0.5f);
                    onCompletionField.style.borderTopLeftRadius = 3;
                    onCompletionField.style.borderTopRightRadius = 3;
                    onCompletionField.style.borderBottomLeftRadius = 3;
                    onCompletionField.style.borderBottomRightRadius = 3;
                    
                    onCompletionField.Bind(serializedDialogue);
                    
                    onCompletionField.TrackPropertyValue(onCompletionProp, prop => 
                    {
                        serializedDialogue.ApplyModifiedProperties();
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                        EditorApplication.delayCall += RebuildOnCompletionField;
                    });
                    
                    onCompletionField.RegisterCallback<SerializedPropertyChangeEvent>(evt =>
                    {
                        serializedDialogue.ApplyModifiedProperties();
                        EditorUtility.SetDirty(dialogue);
                        AssetDatabase.SaveAssets();
                    });
                    
                    onCompletionContainer.Add(onCompletionField);
                    
                    EditorApplication.delayCall += () =>
                    {
                        EditorApplication.delayCall += () =>
                        {
                            
                            var foldout = onCompletionField.Q<Foldout>();
                            if (foldout != null)
                            {
                                foldout.value = true;
                            }
                            else
                            {
                                foreach (var child in onCompletionField.Children())
                                {
                                }
                            }
                        };
                    };
                }
                
                RebuildOnCompletionField();
                node.mainContainer.Add(onCompletionContainer);
            }
            
            var bottomSeparator = new VisualElement
            {
                style = {
                    height = 1,
                    backgroundColor = new Color(1f, 1f, 1f, 0.1f),
                    marginTop = 6,
                    marginBottom = 4,
                    marginLeft = 3,
                    marginRight = 3
                }
            };
            node.mainContainer.Add(bottomSeparator);
            
            var nameLabel = new Label(dialogue.name)
            {
                name = "dialogue-name-label",
                style = {
                    fontSize = 7,
                    opacity = 0.5f,
                    paddingTop = 2,
                    paddingBottom = 4,
                    paddingLeft = 8,
                    minWidth = 200,
                    unityFontStyleAndWeight = FontStyle.Italic
                }
            };
            
            nameLabel.RegisterCallback<MouseDownEvent>(evt =>
            {
                if (evt.clickCount == 1)
                {
                    var nameField = new TextField { value = dialogue.name };
                    nameField.style.fontSize = 8;
                    
                    var parent = nameLabel.parent;
                    int index = parent.IndexOf(nameLabel);
                    parent.Remove(nameLabel);
                    parent.Insert(index, nameField);
                    
                    nameField.Focus();
                    nameField.SelectAll();
                    
                    nameField.RegisterCallback<BlurEvent>(blurEvt =>
                    {
                        SaveDialogueNameAndRevert(nameField, parent, index, dialogue, nameLabel);
                    });
                    
                    nameField.RegisterCallback<KeyDownEvent>(keyEvt =>
                    {
                        if (keyEvt.keyCode == KeyCode.Return || keyEvt.keyCode == KeyCode.KeypadEnter)
                        {
                            SaveDialogueNameAndRevert(nameField, parent, index, dialogue, nameLabel);
                            keyEvt.StopPropagation();
                        }
                        else if (keyEvt.keyCode == KeyCode.Escape)
                        {
                            parent.Remove(nameField);
                            parent.Insert(index, nameLabel);
                            keyEvt.StopPropagation();
                        }
                    });
                }
            });
            
            void SaveDialogueNameAndRevert(TextField field, VisualElement parent, int index, Gyvr.Mythril2D.DialogueSequence dlg, Label label)
            {
                if (!string.IsNullOrWhiteSpace(field.value) && field.value != dlg.name)
                {
                    string assetPath = AssetDatabase.GetAssetPath(dlg);
                    string newFileName = field.value.Replace(" ", "_");
                    string result = AssetDatabase.RenameAsset(assetPath, newFileName);
                    if (string.IsNullOrEmpty(result))
                    {
                        label.text = field.value;
                        AssetDatabase.SaveAssets();
                    }
                }
                parent.Remove(field);
                parent.Insert(index, label);
            }
            
            node.mainContainer.Add(nameLabel);
            
            node.inputContainer.style.display = DisplayStyle.None;
            node.outputContainer.style.display = DisplayStyle.None;
            
            bool isHintOverride = role == "HintOverride_Unconnected" || role.StartsWith("HintOverride_");
            if (!isDefaultHint && !isHintOverride)
            {
                var inputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, typeof(bool));
                inputPort.portName = "";
                // Input port color matches title bar or orange for grey
                if (label.Contains("Completed"))
                    inputPort.portColor = new Color(0.2f, 0.6f, 0.3f);
                else if (label.Contains("Offer"))
                    inputPort.portColor = new Color(0.5f, 0.7f, 1f);
                else if (label.Contains("Hint"))
                    inputPort.portColor = new Color(0.9f, 0.6f, 0.2f);
                else
                    inputPort.portColor = new Color(1f, 0.6f, 0.2f); // Orange for grey/default
                inputPort.style.position = Position.Absolute;
                inputPort.style.left = 2;
                inputPort.style.top = 10;
                node.Add(inputPort);
            }
            
            if (isHintOverride)
            {
                var inputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(bool));
                inputPort.portName = "";
                inputPort.portColor = new Color(0.9f, 0.6f, 0.2f);
                inputPort.style.position = Position.Absolute;
                inputPort.style.left = 2;
                inputPort.style.top = 10;
                node.Add(inputPort);
            }
            
            if (!isDefaultHint && !isHintOverride && (dialogue.options == null || dialogue.options.Length == 0) && role != "QuestOffer" && role != "QuestCompleted")
            {
                var singleOutput = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(bool));
                singleOutput.portName = "";
                singleOutput.portColor = new Color(0.6f, 0.4f, 0.8f); // Purple for dialogue output
                singleOutput.style.position = Position.Absolute;
                singleOutput.style.right = 2;
                singleOutput.style.top = 10;
                
                node.Add(singleOutput);
            }
            
            AddElement(node);
            m_nodeViews[nodeId] = node;
            
            SaveNodeData(nodeId, "Dialogue", dialogue.name, position, role);
            
            node.RegisterCallback<GeometryChangedEvent>(evt =>
            {
                if (m_graphData != null)
                {
                    var nodeData = m_graphData.nodes.FirstOrDefault(n => n.nodeId == nodeId);
                    if (nodeData != null)
                    {
                        var newPos = node.GetPosition().position;
                        if (Vector2.Distance(nodeData.position, newPos) > 1f)
                        {
                            nodeData.position = newPos;
                            EditorUtility.SetDirty(m_graphData);
                        }
                    }
                }
            });

            node.RegisterCallback<ContextClickEvent>(evt =>
            {
                ShowDialogueRoleMenu(node, nodeId);
                evt.StopPropagation();
            });

            return node;
        }

        private void RefreshDialogueNode(Node oldNode, Gyvr.Mythril2D.DialogueSequence dialogue)
        {
            
            Rect oldPos = oldNode.GetPosition();
            string nodeId = oldNode.name;
            string label = oldNode.title;

            var nodeData = m_graphData.GetNode(nodeId);
            string role = nodeData?.role ?? "";
            
            var incomingConnections = new List<(string fromNodeId, Port outputPort, string inputPortName)>();
            var outgoingConnections = new List<(string toNodeId, Port inputPort, string outputPortName)>();
            
            foreach (var edge in edges.ToList())
            {
                if (edge.input?.node == oldNode && edge.output?.node != null)
                {
                    var fromNode = edge.output.node as Node;
                    if (fromNode != null)
                    {
                        incomingConnections.Add((fromNode.name, edge.output, edge.input.portName ?? ""));
                    }
                }
                if (edge.output?.node == oldNode && edge.input?.node != null)
                {
                    var toNode = edge.input.node as Node;
                    if (toNode != null)
                    {
                        outgoingConnections.Add((toNode.name, edge.input, edge.output.portName ?? ""));
                    }
                }
            }

            foreach (var edge in edges.ToList())
            {
                if (edge.input?.node == oldNode || edge.output?.node == oldNode)
                {
                    RemoveElement(edge);
                }
            }
            
            RemoveElement(oldNode);
            m_nodeViews.Remove(nodeId);
            
            var newNode = CreateDialogueNode(dialogue, label, oldPos.position, nodeId, role);
            
            if (newNode == null)
            {
                return;
            }

            EditorApplication.delayCall += () =>
            {
                if (newNode == null || newNode.parent == null)
                {
                    return;
                }
                
                newNode.SetPosition(oldPos);
                var actualPos = newNode.GetPosition();
                
                if (nodeData != null)
                {
                    nodeData.position = oldPos.position;
                    EditorUtility.SetDirty(m_graphData);
                }

                int inputPortCount = 0;
                int outputPortCount = 0;
                newNode.Query<Port>().ForEach(p =>
                {
                    if (p.direction == Direction.Input) inputPortCount++;
                    if (p.direction == Direction.Output) outputPortCount++;
                });
                
                if (inputPortCount == 0 && outputPortCount == 0)
                {
                }
                
                foreach (var (fromNodeId, outputPort, inputPortName) in incomingConnections)
                {
                    if (outputPort != null && newNode != null)
                    {
                        Port inputPort = null;
                        if (!string.IsNullOrEmpty(inputPortName))
                        {
                            newNode.Query<Port>().ForEach(p =>
                            {
                                if (p.direction == Direction.Input && p.portName == inputPortName && inputPort == null)
                                    inputPort = p;
                            });
                        }
                        
                        if (inputPort == null)
                        {
                            newNode.Query<Port>().ForEach(p =>
                            {
                                if (p.direction == Direction.Input && inputPort == null)
                                    inputPort = p;
                            });
                        }
                        
                        if (inputPort != null && outputPort.node != null)
                        {
                            var newEdge = outputPort.ConnectTo(inputPort);
                            AddElement(newEdge);
                            
                            if (m_graphData != null)
                            {
                                var connectionData = new ConnectionData
                                {
                                    fromNodeId = fromNodeId,
                                    toNodeId = nodeId,
                                    fromPortName = outputPort.portName ?? "",
                                    toPortName = inputPort.portName ?? ""
                                };
                                if (!m_graphData.connections.Any(c => c.fromNodeId == fromNodeId && c.toNodeId == nodeId))
                                {
                                    m_graphData.AddConnection(connectionData);
                                }
                            }
                        }
                        else
                        {
                        }
                    }
                }
                
                foreach (var (toNodeId, inputPort, outputPortName) in outgoingConnections)
                {
                    if (inputPort != null && newNode != null)
                    {
                        Port outputPort = null;
                        if (!string.IsNullOrEmpty(outputPortName))
                        {
                            newNode.Query<Port>().ForEach(p =>
                            {
                                if (p.direction == Direction.Output && p.portName == outputPortName && outputPort == null)
                                    outputPort = p;
                            });
                        }
                        
                        if (outputPort == null)
                        {
                            newNode.Query<Port>().ForEach(p =>
                            {
                                if (p.direction == Direction.Output && outputPort == null)
                                    outputPort = p;
                            });
                        }
                        
                        if (outputPort != null && inputPort.node != null)
                        {
                            var newEdge = outputPort.ConnectTo(inputPort);
                            AddElement(newEdge);
                            
                            if (m_graphData != null)
                            {
                                var connectionData = new ConnectionData
                                {
                                    fromNodeId = nodeId,
                                    toNodeId = toNodeId,
                                    fromPortName = outputPort.portName ?? "",
                                    toPortName = inputPort.portName ?? ""
                                };
                                if (!m_graphData.connections.Any(c => c.fromNodeId == nodeId && c.toNodeId == toNodeId && c.fromPortName == outputPortName))
                                {
                                    m_graphData.AddConnection(connectionData);
                                }
                            }
                        }
                        else
                        {
                        }
                    }
                }
                
                if (m_graphData != null)
                {
                    EditorUtility.SetDirty(m_graphData);
                    AssetDatabase.SaveAssets();
                }
                
            };
        }

        private void ShowDialogueRoleMenu(Node node, string nodeId)
        {
            var menu = new GenericMenu();
            
            var nodeData = m_graphData.GetNode(nodeId);
            string currentRole = nodeData?.role ?? "";
            
            menu.AddItem(new GUIContent("Role/Offer"), currentRole == "QuestOffer", () => SetDialogueRole(nodeId, "QuestOffer", node));
            menu.AddItem(new GUIContent("Role/Hint"), currentRole == "QuestHint", () => SetDialogueRole(nodeId, "QuestHint", node));
            menu.AddItem(new GUIContent("Role/Completed"), currentRole == "QuestCompleted", () => SetDialogueRole(nodeId, "QuestCompleted", node));
            menu.AddItem(new GUIContent("Role/None"), currentRole == "", () => SetDialogueRole(nodeId, "", node));
            
            menu.ShowAsContext();
        }

        private void SetDialogueRole(string nodeId, string role, Node node)
        {
            var nodeData = m_graphData.GetNode(nodeId);
            if (nodeData != null)
            {
                nodeData.role = role;
                EditorUtility.SetDirty(m_graphData);
                AssetDatabase.SaveAssets();
                
                Color roleColor = role switch
                {
                    "QuestOffer" => new Color(0.5f, 0.7f, 1f, 0.4f),
                    "QuestHint" => new Color(0.8f, 0.7f, 0.3f, 0.4f),
                    "QuestCompleted" => new Color(0.2f, 0.6f, 0.3f, 0.5f),
                    _ => new Color(0.4f, 0.4f, 0.4f, 0.3f)
                };
                node.titleContainer.style.backgroundColor = roleColor;
                
            }
        }

        private void CreateDialogueTreeBranches(Gyvr.Mythril2D.DialogueSequence rootDialogue, Vector2 startPosition)
        {
            if (rootDialogue == null || rootDialogue.options == null || rootDialogue.options.Length == 0)
                return;
            
            var createdDialogues = new System.Collections.Generic.HashSet<int>();
            CreateDialogueTreeBranchesRecursive(rootDialogue, startPosition, createdDialogues, 0);
        }
        
        private void CreateDialogueTreeBranchesRecursive(Gyvr.Mythril2D.DialogueSequence dialogue, Vector2 position, System.Collections.Generic.HashSet<int> created, int depth)
        {
            if (dialogue == null || dialogue.options == null || depth > 10)
                return;
            
            float xOffset = 250f;
            float ySpacing = 180f;
            
            for (int i = 0; i < dialogue.options.Length; i++)
            {
                var option = dialogue.options[i];
                if (option.sequence == null)
                    continue;
                
                int dialogueId = option.sequence.GetInstanceID();
                
                if (created.Contains(dialogueId))
                    continue;
                
                created.Add(dialogueId);
                
                Vector2 branchPos = new Vector2(position.x + xOffset, position.y + (i * ySpacing));
                
                string branchLabel = string.IsNullOrEmpty(option.name) ? "Branch" : option.name;
                var branchNode = CreateDialogueNode(option.sequence, branchLabel, branchPos);
                
                string parentNodeId = $"dialogue-{dialogue.GetInstanceID()}";
                if (m_nodeViews.TryGetValue(parentNodeId, out Node parentNode))
                {
                    string optionPortName = $"option-{i}";
                    var outputPort = FindPort(parentNode, Direction.Output, optionPortName);
                    var inputPort = FindPort(branchNode, Direction.Input, "");
                    
                    if (outputPort != null && inputPort != null)
                    {
                        CreateAndAddEdge(outputPort, inputPort);
                    }
                }
                
                CreateDialogueTreeBranchesRecursive(option.sequence, branchPos, created, depth + 1);
            }
        }

        private string GetTaskNodeTitle(QuestTask task, int taskIndex)
        {
            try
            {
                string taskType = task.GetType().Name;
                
                var titleField = task.GetType().BaseType?.GetField("m_title", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                string titleFormat = titleField?.GetValue(task) as string ?? "";
                
                if (taskType == "ItemTask")
                {
                    var itemField = task.GetType().GetField("item");
                    var amountField = task.GetType().GetField("amountToCollect");
                    if (itemField != null && amountField != null)
                    {
                        var item = itemField.GetValue(task);
                        var amount = amountField.GetValue(task);
                        if (item != null && !string.IsNullOrEmpty(titleFormat))
                        {
                            var displayNameProp = item.GetType().GetProperty("displayName");
                            string itemName = displayNameProp?.GetValue(item) as string ?? "Unknown Item";
                            return string.Format(titleFormat, itemName, amount, amount);
                        }
                    }
                    return "Item Task";
                }
                else if (taskType == "KillMonsterTask")
                {
                    var monsterField = task.GetType().GetField("monster");
                    var countField = task.GetType().GetField("monstersToKill");
                    if (monsterField != null && countField != null)
                    {
                        var monster = monsterField.GetValue(task);
                        var count = countField.GetValue(task);
                        if (monster != null && !string.IsNullOrEmpty(titleFormat))
                        {
                            var displayNameProp = monster.GetType().GetProperty("displayName");
                            string monsterName = displayNameProp?.GetValue(monster) as string ?? "Unknown Monster";
                            return string.Format(titleFormat, monsterName, count, count);
                        }
                    }
                    return "Kill Monster Task";
                }
                else if (taskType == "TalkToNPCTask")
                {
                    var targetField = task.GetType().GetField("target");
                    if (targetField != null)
                    {
                        var target = targetField.GetValue(task);
                        if (target != null && !string.IsNullOrEmpty(titleFormat))
                        {
                            var displayNameProp = target.GetType().GetProperty("displayName");
                            string npcName = displayNameProp?.GetValue(target) as string ?? "Unknown NPC";
                            return string.Format(titleFormat, npcName);
                        }
                    }
                    return "Talk to NPC Task";
                }
                else if (taskType == "GameFlagTask")
                {
                    var flagsField = task.GetType().GetField("gameFlags");
                    if (flagsField != null)
                    {
                        var flags = flagsField.GetValue(task) as System.Collections.IDictionary;
                        if (flags != null && flags.Count > 0 && !string.IsNullOrEmpty(titleFormat))
                        {
                            int flagCount = flags.Count;
                            return string.Format(titleFormat, flagCount, flagCount);
                        }
                    }
                    return "Game Flag Task";
                }
                
                if (!string.IsNullOrEmpty(titleFormat))
                {
                    return titleFormat;
                }
            }
            catch (System.Exception)
            {
            }
            
            return $"{task.GetType().Name.Replace("Task", "")} ({taskIndex + 1})";
        }

        private string GetTaskNodeDescription(QuestTask task)
        {
            try
            {
                string taskType = task.GetType().Name.Replace("Task", "");
                System.Text.StringBuilder desc = new System.Text.StringBuilder();
                
                bool hasDetails = false;
                
                if (taskType == "Item")
                {
                    var itemField = task.GetType().GetField("item");
                    var amountField = task.GetType().GetField("amountToCollect");
                    if (itemField != null)
                    {
                        var item = itemField.GetValue(task);
                        if (item != null)
                        {
                            var displayNameProp = item.GetType().GetProperty("displayName");
                            string itemName = displayNameProp?.GetValue(item) as string ?? "Unknown Item";
                            desc.AppendLine($"Item: {itemName}");
                            hasDetails = true;
                        }
                    }
                    if (amountField != null)
                    {
                        desc.AppendLine($"Amount: {amountField.GetValue(task)}");
                        hasDetails = true;
                    }
                }
                else if (taskType == "KillMonster")
                {
                    var monsterField = task.GetType().GetField("monster");
                    var countField = task.GetType().GetField("monstersToKill");
                    if (monsterField != null)
                    {
                        var monster = monsterField.GetValue(task);
                        if (monster != null)
                        {
                            var displayNameProp = monster.GetType().GetProperty("displayName");
                            string monsterName = displayNameProp?.GetValue(monster) as string ?? "Unknown Monster";
                            desc.AppendLine($"Target: {monsterName}");
                            hasDetails = true;
                        }
                    }
                    if (countField != null)
                    {
                        desc.AppendLine($"Count: {countField.GetValue(task)}");
                        hasDetails = true;
                    }
                }
                else if (taskType == "TalkToNPC")
                {
                    var targetField = task.GetType().GetField("target");
                    var dialogueField = task.GetType().GetField("dialogue");
                    if (targetField != null)
                    {
                        var target = targetField.GetValue(task);
                        if (target != null)
                        {
                            var displayNameProp = target.GetType().GetProperty("displayName");
                            string npcName = displayNameProp?.GetValue(target) as string ?? "Unknown NPC";
                            desc.AppendLine($"NPC: {npcName}");
                            hasDetails = true;
                        }
                    }
                    if (dialogueField != null)
                    {
                        var dialogue = dialogueField.GetValue(task);
                        if (dialogue != null)
                        {
                            string dialogueName = (dialogue as UnityEngine.Object)?.name ?? "Unknown Dialogue";
                            desc.AppendLine($"Dialogue: {dialogueName}");
                            hasDetails = true;
                        }
                    }
                }
                else if (taskType == "GameFlag")
                {
                    var flagsField = task.GetType().GetField("gameFlags");
                    if (flagsField != null)
                    {
                        var flags = flagsField.GetValue(task) as System.Collections.IDictionary;
                        if (flags != null && flags.Count > 0)
                        {
                            desc.AppendLine($"Conditions: {flags.Count}");
                            hasDetails = true;
                            int shown = 0;
                            foreach (System.Collections.DictionaryEntry flag in flags)
                            {
                                if (shown < 3)
                                {
                                    desc.AppendLine($"  {flag.Key} = {flag.Value}");
                                    shown++;
                                }
                            }
                            if (flags.Count > 3)
                            {
                                desc.AppendLine($"  ... and {flags.Count - 3} more");
                            }
                        }
                    }
                }
                
                var requirePrevField = task.GetType().GetField("requirePreviousTaskCompletion");
                if (requirePrevField != null)
                {
                    bool requirePrev = (bool)requirePrevField.GetValue(task);
                    if (requirePrev)
                    {
                        desc.AppendLine("⚠ Requires previous task");
                        hasDetails = true;
                    }
                }
                
                if (!hasDetails)
                {
                    return "";
                }
                
                return desc.ToString().TrimEnd();
            }
            catch (System.Exception)
            {
                return task.GetType().Name.Replace("Task", "");
            }
        }

        private void AddTaskField(Node node, SerializedObject serializedTask, string fieldName, string label)
        {
            var prop = serializedTask.FindProperty(fieldName);
            if (prop == null) return;
            
            var labelElement = new Label(label)
            {
                style = {
                    fontSize = 7,
                    opacity = 0.6f,
                    paddingLeft = 8,
                    paddingTop = 3,
                    paddingBottom = 1,
                    unityFontStyleAndWeight = FontStyle.Bold
                }
            };
            node.mainContainer.Add(labelElement);
            
            var field = new PropertyField(prop, "");
            field.style.fontSize = 7;
            field.style.paddingLeft = 8;
            field.style.paddingRight = 8;
            field.style.paddingTop = 2;
            field.style.paddingBottom = 2;
            
            if (fieldName == "gameFlags")
            {
                field.style.minWidth = 300;
                field.style.maxWidth = 340;
                field.style.minHeight = 60;
            }
            else
            {
                field.style.minWidth = 280;
                field.style.maxWidth = 320;
            }
            
            field.style.backgroundColor = new Color(0.15f, 0.15f, 0.15f, 0.5f);
            field.style.borderTopLeftRadius = 3;
            field.style.borderTopRightRadius = 3;
            field.style.borderBottomLeftRadius = 3;
            field.style.borderBottomRightRadius = 3;
            field.RegisterCallback<ChangeEvent<SerializedProperty>>(evt =>
            {
                serializedTask.ApplyModifiedProperties();
                AssetDatabase.SaveAssets();
            });
            field.Bind(serializedTask);
            
            if (fieldName == "gameFlags")
            {
                field.schedule.Execute(() => 
                {
                    var foldout = field.Q<Foldout>();
                    if (foldout != null)
                    {
                        foldout.style.paddingLeft = 0;
                        var toggle = foldout.Q<Toggle>();
                        if (toggle != null)
                        {
                            toggle.style.marginLeft = -8;
                        }
                    }
                });
            }
            
            node.mainContainer.Add(field);
        }

        private void AddNPCPickerButton(Node node, SerializedObject serializedTask, TalkToNPCTask talkToNPCTask)
        {
            var buttonContainer = new VisualElement
            {
                style = {
                    flexDirection = FlexDirection.Row,
                    paddingLeft = 8,
                    paddingRight = 8,
                    paddingTop = 2,
                    paddingBottom = 4
                }
            };
            
            var pickerButton = new Button(() =>
            {
                int controlId = GUIUtility.GetControlID(FocusType.Passive);
                EditorGUIUtility.ShowObjectPicker<Gyvr.Mythril2D.NPCSheet>(
                    talkToNPCTask?.target, 
                    false, 
                    "CS_", 
                    controlId);
                
                EditorApplication.update += OnPickerUpdate;
                
                void OnPickerUpdate()
                {
                    if (Event.current?.commandName == "ObjectSelectorUpdated" || 
                        EditorGUIUtility.GetObjectPickerControlID() == controlId)
                    {
                        var selected = EditorGUIUtility.GetObjectPickerObject() as Gyvr.Mythril2D.NPCSheet;
                        if (selected != null && talkToNPCTask != null)
                        {
                            Undo.RecordObject(talkToNPCTask, "Set NPC Target");
                            talkToNPCTask.target = selected;
                            EditorUtility.SetDirty(talkToNPCTask);
                            serializedTask.Update();
                            AssetDatabase.SaveAssets();
                            
                            // Update node title
                            var titleLabel = node.titleContainer.Q<Label>();
                            if (titleLabel != null)
                            {
                                int taskIndex = -1;
                                if (node.userData is System.Tuple<QuestTask, int> taskData)
                                {
                                    taskIndex = taskData.Item2;
                                }
                                titleLabel.text = GetTaskNodeTitle(talkToNPCTask, taskIndex);
                            }
                        }
                    }
                    
                    if (EditorGUIUtility.GetObjectPickerControlID() != controlId)
                    {
                        EditorApplication.update -= OnPickerUpdate;
                    }
                }
            })
            {
                text = "Select NPC...",
                style = {
                    height = 18,
                    fontSize = 10,
                    flexGrow = 1
                }
            };
            
            buttonContainer.Add(pickerButton);
            node.mainContainer.Add(buttonContainer);
        }

        private void SaveNodeData(string nodeId, string nodeType, string dataReference, Vector2 position, string role = "")
        {
            var nodeData = new NodeData
            {
                nodeId = nodeId,
                nodeType = nodeType,
                dataReference = dataReference,
                position = position,
                role = role
            };

            m_graphData.SetNode(nodeData);
            EditorUtility.SetDirty(m_graphData);
        }

        private void SaveNodeCustomTitle(string nodeId, string customTitle)
        {
            if (m_graphData == null) return;
            
            var nodeData = m_graphData.GetNode(nodeId);
            if (nodeData != null)
            {
                if (nodeData.metadata == null)
                    nodeData.metadata = new List<MetadataEntry>();
                
                var existing = nodeData.metadata.Find(m => m.key == "customTitle");
                if (existing != null)
                    existing.value = customTitle;
                else
                    nodeData.metadata.Add(new MetadataEntry { key = "customTitle", value = customTitle });
                
                EditorUtility.SetDirty(m_graphData);
                AssetDatabase.SaveAssets();
            }
        }

        private void SaveGraphData()
        {
            if (m_graphData != null)
            {
                SaveGraph(m_graphData);
            }
        }

        public void SaveGraph(QuestGraphData graphData)
        {
            if (graphData == null) return;

            var container = contentViewContainer;
            var translate = container.resolvedStyle.translate;
            graphData.viewPosition = new Vector3(translate.x, translate.y, 0);
            var scale = container.resolvedStyle.scale;
            var scaleVec = scale.value;
            graphData.viewScale = new Vector3(scaleVec.x, scaleVec.y, scaleVec.z);

            int positionsSaved = 0;
            foreach (var kvp in m_nodeViews)
            {
                var nodeData = graphData.GetNode(kvp.Key);
                if (nodeData != null)
                {
                    Vector2 newPos = kvp.Value.GetPosition().position;
                    nodeData.position = newPos;
                    positionsSaved++;
                }
            }

            graphData.stickyNotes.Clear();
            var stickyNotes = this.Query<StickyNote>().ToList();
            foreach (var note in stickyNotes)
            {
                var noteData = new StickyNoteData
                {
                    position = note.GetPosition().position,
                    contents = note.contents
                };
                graphData.stickyNotes.Add(noteData);
            }

            graphData.ApplyDialogueRolesToQuest(m_quest);

            EditorUtility.SetDirty(graphData);
            EditorUtility.SetDirty(m_quest);
            AssetDatabase.SaveAssets();
        }

        public void AutoLayout()
        {
            var layout = CalculateNodeLayout();
            
            foreach (var kvp in layout)
            {
                if (m_nodeViews.TryGetValue(kvp.Key, out Node node))
                {
                    node.SetPosition(new Rect(kvp.Value, Vector2.zero));
                }
            }
            
            if (m_graphData != null)
            {
                foreach (var nodeData in m_graphData.nodes)
                {
                    if (layout.TryGetValue(nodeData.nodeId, out Vector2 pos))
                    {
                        nodeData.position = pos;
                    }
                }
                EditorUtility.SetDirty(m_graphData);
                AssetDatabase.SaveAssets();
            }
        }

        private Dictionary<string, Vector2> CalculateNodeLayout()
        {
            var positions = new Dictionary<string, Vector2>();
            const float HORIZONTAL_SPACING = 450f;
            const float VERTICAL_SPACING = 150f;
            const float START_X = 50f;
            const float START_Y = 50f;

            var outgoingConnections = new Dictionary<string, List<string>>();
            foreach (var edge in edges)
            {
                var outputNode = edge.output?.node as Node;
                var inputNode = edge.input?.node as Node;
                
                if (outputNode != null && inputNode != null)
                {
                    string fromId = outputNode.name;
                    string toId = inputNode.name;
                    
                    if (!outgoingConnections.ContainsKey(fromId))
                        outgoingConnections[fromId] = new List<string>();
                    if (!outgoingConnections[fromId].Contains(toId))
                        outgoingConnections[fromId].Add(toId);
                }
            }

            var nodeColumns = new Dictionary<string, int>();
            var columnNodes = new Dictionary<int, List<string>>();

            var column0Nodes = new List<string>();
            if (m_nodeViews.ContainsKey("quest-info"))
                column0Nodes.Add("quest-info");
            if (m_nodeViews.ContainsKey("hint-default"))
                column0Nodes.Add("hint-default");
            
            foreach (var nodeId in m_nodeViews.Keys)
            {
                if (nodeId.StartsWith("hint-") && nodeId != "hint-default")
                    column0Nodes.Add(nodeId);
            }

            foreach (var nodeId in column0Nodes)
            {
                nodeColumns[nodeId] = 0;
            }
            columnNodes[0] = column0Nodes;

            var queue = new Queue<string>();
            var visited = new HashSet<string>(column0Nodes);
            
            foreach (var startNode in column0Nodes)
                queue.Enqueue(startNode);

            while (queue.Count > 0)
            {
                string currentId = queue.Dequeue();
                int currentColumn = nodeColumns[currentId];

                if (outgoingConnections.ContainsKey(currentId))
                {
                    foreach (var connectedId in outgoingConnections[currentId])
                    {
                        if (!visited.Contains(connectedId))
                        {
                            visited.Add(connectedId);
                            int nextColumn = currentColumn + 1;
                            nodeColumns[connectedId] = nextColumn;
                            
                            if (!columnNodes.ContainsKey(nextColumn))
                                columnNodes[nextColumn] = new List<string>();
                            columnNodes[nextColumn].Add(connectedId);
                            
                            queue.Enqueue(connectedId);
                        }
                    }
                }
            }

            var unconnectedNodes = m_nodeViews.Keys.Where(id => !visited.Contains(id)).ToList();
            if (unconnectedNodes.Count > 0)
            {
                int orphanColumn = columnNodes.Keys.Max() + 2;
                columnNodes[orphanColumn] = unconnectedNodes;
                foreach (var nodeId in unconnectedNodes)
                {
                    nodeColumns[nodeId] = orphanColumn;
                }
            }

            foreach (var columnKvp in columnNodes.OrderBy(c => c.Key))
            {
                int column = columnKvp.Key;
                var nodesInColumn = columnKvp.Value;
                
                float x = START_X + (column * HORIZONTAL_SPACING);
                float y = START_Y;

                foreach (var nodeId in nodesInColumn)
                {
                    positions[nodeId] = new Vector2(x, y);
                    y += VERTICAL_SPACING;
                }
            }

            return positions;
        }

        public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
        {
            return ports.ToList().Where(endPort =>
            {
                // Basic checks: different direction, different node
                if (endPort.direction == startPort.direction) return false;
                if (endPort.node == startPort.node) return false;
                
                var startNode = startPort.node as Node;
                var endNode = endPort.node as Node;
                
                // If dragging FROM a dialogue node's output
                if (startPort.direction == Direction.Output && 
                    startNode?.userData is Gyvr.Mythril2D.DialogueSequence)
                {
                    // If trying to connect TO another dialogue node
                    if (endNode?.userData is Gyvr.Mythril2D.DialogueSequence)
                    {
                        // Only allow if the output is an option port (not the main output)
                        if (string.IsNullOrEmpty(startPort.portName) || !startPort.portName.StartsWith("option-"))
                        {
                            return false;
                        }
                    }
                }
                
                // If dragging TO a dialogue node's input
                if (endPort.direction == Direction.Input && 
                    endNode?.userData is Gyvr.Mythril2D.DialogueSequence)
                {
                    // If coming FROM another dialogue node
                    if (startNode?.userData is Gyvr.Mythril2D.DialogueSequence)
                    {
                        // Only allow if the output is an option port
                        if (string.IsNullOrEmpty(startPort.portName) || !startPort.portName.StartsWith("option-"))
                        {
                            return false;
                        }
                    }
                }
                
                return true;
            }).ToList();
        }

        private void CreateStickyNote(Vector2 position)
        {
            var stickyNote = new StickyNote()
            {
                title = "",
                contents = "Sticky Note"
            };
            stickyNote.SetPosition(new Rect(position, Vector2.zero));
            
            stickyNote.style.minHeight = 40;
            stickyNote.style.minWidth = 120;
            
            stickyNote.style.fontSize = 11;
            
            var textField = stickyNote.Q<TextField>();
            if (textField != null)
            {
                textField.style.fontSize = 11;
                textField.multiline = true;
                
                var textElement = textField.Q<TextElement>();
                if (textElement != null)
                {
                    textElement.style.fontSize = 11;
                    textElement.style.unityFontStyleAndWeight = FontStyle.Normal;
                }
                
                var textInput = textField.Q(className: "unity-text-element");
                if (textInput != null)
                {
                    textInput.style.fontSize = 11;
                    textInput.style.unityFontStyleAndWeight = FontStyle.Normal;
                }
                
                textField.RegisterCallback<ChangeEvent<string>>(evt =>
                {
                    if (m_graphData != null)
                    {
                        var notes = m_graphData.stickyNotes;
                        var notePos = stickyNote.GetPosition().position;
                        
                        for (int i = 0; i < notes.Count; i++)
                        {
                            if (notes[i].position == notePos)
                            {
                                notes[i].contents = evt.newValue;
                                EditorUtility.SetDirty(m_graphData);
                                break;
                            }
                        }
                    }
                });
            }
            
            AddElement(stickyNote);
            
            stickyNote.RegisterCallback<ContextualMenuPopulateEvent>(evt =>
            {
                evt.menu.MenuItems().Clear();
                evt.StopPropagation();
            });
            
            if (m_graphData != null)
            {
                var noteData = new StickyNoteData
                {
                    position = position,
                    contents = "Sticky Note"
                };
                m_graphData.stickyNotes.Add(noteData);
                EditorUtility.SetDirty(m_graphData);
            }
            
        }
        
        private void RestoreStickyNotes()
        {
            if (m_graphData == null || m_graphData.stickyNotes == null)
                return;
            
            foreach (var noteData in m_graphData.stickyNotes)
            {
                var stickyNote = new StickyNote()
                {
                    title = "",
                    contents = noteData.contents
                };
                stickyNote.SetPosition(new Rect(noteData.position, Vector2.zero));
                stickyNote.style.minHeight = 40;
                stickyNote.style.minWidth = 120;
                
                stickyNote.style.fontSize = 11;
                
                var textField = stickyNote.Q<TextField>();
                if (textField != null)
                {
                    textField.style.fontSize = 11;
                    textField.multiline = true;
                    
                    var textElement = textField.Q<TextElement>();
                    if (textElement != null)
                    {
                        textElement.style.fontSize = 11;
                        textElement.style.unityFontStyleAndWeight = FontStyle.Normal;
                    }
                    
                    var textInput = textField.Q(className: "unity-text-element");
                    if (textInput != null)
                    {
                        textInput.style.fontSize = 11;
                        textInput.style.unityFontStyleAndWeight = FontStyle.Normal;
                    }
                    
                    textField.RegisterCallback<ChangeEvent<string>>(evt =>
                    {
                        if (m_graphData != null)
                        {
                            var notes = m_graphData.stickyNotes;
                            var notePos = stickyNote.GetPosition().position;
                            
                            for (int i = 0; i < notes.Count; i++)
                            {
                                if (notes[i].position == notePos)
                                {
                                    notes[i].contents = evt.newValue;
                                    EditorUtility.SetDirty(m_graphData);
                                    break;
                                }
                            }
                        }
                    });
                }
                
                AddElement(stickyNote);
                
                stickyNote.RegisterCallback<ContextualMenuPopulateEvent>(evt =>
                {
                    evt.menu.MenuItems().Clear();
                    evt.StopPropagation();
                });
            }
            
        }

        public void ZoomToFit()
        {
            if (m_nodeViews.Count == 0)
            {
                return;
            }

            Rect bounds = Rect.zero;
            bool first = true;

            foreach (var node in m_nodeViews.Values)
            {
                Rect nodeRect = node.GetPosition();
                if (first)
                {
                    bounds = nodeRect;
                    first = false;
                }
                else
                {
                    bounds = RectUtils.Encompass(bounds, nodeRect);
                }
            }

            bounds.x -= 100;
            bounds.y -= 100;
            bounds.width += 200;
            bounds.height += 200;

            FrameAll();
            
        }
    }

    public class LeftMousePanManipulator : Manipulator
    {
        private Vector2 m_lastMousePos;
        private bool m_isPanning;

        protected override void RegisterCallbacksOnTarget()
        {
            target.RegisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
            target.RegisterCallback<MouseMoveEvent>(OnMouseMove);
            target.RegisterCallback<MouseUpEvent>(OnMouseUp);
        }

        protected override void UnregisterCallbacksFromTarget()
        {
            target.UnregisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
            target.UnregisterCallback<MouseMoveEvent>(OnMouseMove);
            target.UnregisterCallback<MouseUpEvent>(OnMouseUp);
        }

        private void OnMouseDown(MouseDownEvent evt)
        {
            if (evt.button == 0 && evt.target is GraphView)
            {
                m_isPanning = true;
                m_lastMousePos = evt.mousePosition;
                target.CaptureMouse();
                evt.StopPropagation();
            }
        }

        private void OnMouseMove(MouseMoveEvent evt)
        {
            if (m_isPanning && target.HasMouseCapture())
            {
                Vector2 delta = evt.mousePosition - m_lastMousePos;
                
                var graphView = target as GraphView;
                if (graphView != null)
                {
                    var currentTranslate = graphView.contentViewContainer.resolvedStyle.translate;
                    graphView.contentViewContainer.style.translate = new Translate(currentTranslate.x + delta.x, currentTranslate.y + delta.y);
                }
                
                m_lastMousePos = evt.mousePosition;
                evt.StopPropagation();
            }
        }

        private void OnMouseUp(MouseUpEvent evt)
        {
            if (evt.button == 0 && m_isPanning)
            {
                m_isPanning = false;
                target.ReleaseMouse();
                evt.StopPropagation();
            }
        }
    }
}

