using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;

namespace M2DVisualQuestEditor
{
    public class QuestGraphEditorWindow : EditorWindow
    {
        private QuestGraphView m_graphView;
        private QuestGraphData m_currentGraphData;
        private Gyvr.Mythril2D.Quest m_currentQuest;

        [MenuItem("Window/Mythril2D/Visual Quest Editor")]
        public static void OpenWindow()
        {
            QuestGraphEditorWindow window = GetWindow<QuestGraphEditorWindow>();
            window.titleContent = new GUIContent("Quest Visual Editor");
            window.minSize = new Vector2(800, 600);
        }

        public static void OpenQuest(Gyvr.Mythril2D.Quest quest, bool forceNewWindow = false)
        {
            QuestGraphEditorWindow window;
            
            if (forceNewWindow)
            {
                window = CreateInstance<QuestGraphEditorWindow>();
                window.Show();
            }
            else
            {
                window = GetWindow<QuestGraphEditorWindow>();
            }
            
            window.titleContent = new GUIContent($"Quest: {quest.title}");
            window.minSize = new Vector2(800, 600);
            window.LoadQuest(quest);
        }

        private void OnEnable()
        {
            ConstructGraphView();
            GenerateToolbar();
        }

        private void OnDisable()
        {
            if (m_graphView != null)
            {
                m_graphView.Cleanup();
                rootVisualElement.Remove(m_graphView);
            }
        }

        private void ConstructGraphView()
        {
            m_graphView = new QuestGraphView(this)
            {
                name = "Quest Graph",
                style = { flexGrow = 1 }
            };

            rootVisualElement.Add(m_graphView);
        }

        private void GenerateToolbar()
        {
            var toolbar = new VisualElement
            {
                style =
                {
                    flexDirection = FlexDirection.Row,
                    backgroundColor = new Color(0.25f, 0.25f, 0.25f),
                    paddingTop = 5,
                    paddingBottom = 5,
                    paddingLeft = 5,
                    paddingRight = 5
                }
            };

            var newQuestButton = new Button(() => CreateNewQuest())
            {
                text = "New Quest",
                style = { marginRight = 10 }
            };
            toolbar.Add(newQuestButton);

            var questLabel = new Label("No Quest Loaded")
            {
                name = "quest-label",
                style = { 
                    paddingLeft = 10,
                    paddingRight = 10,
                    unityFontStyleAndWeight = FontStyle.Bold,
                    color = Color.white,
                    alignItems = Align.Center,
                    justifyContent = Justify.FlexStart,
                    flexGrow = 0
                }
            };
            toolbar.Add(questLabel);
            
            var warningContainer = new VisualElement
            {
                name = "warning-container",
                style = {
                    flexDirection = FlexDirection.Row,
                    alignItems = Align.Center,
                    paddingLeft = 10,
                    display = DisplayStyle.None
                }
            };
            
            var warningIcon = new Label("⚠")
            {
                style = {
                    fontSize = 14,
                    color = Color.white,
                    marginRight = 6
                }
            };
            warningContainer.Add(warningIcon);
            
            var warningText = new Label("Some nodes require attention")
            {
                style = {
                    fontSize = 11,
                    color = new Color(0.9f, 0.9f, 0.9f),
                    opacity = 0.9f
                }
            };
            warningContainer.Add(warningText);
            toolbar.Add(warningContainer);

            var spacer = new VisualElement { style = { flexGrow = 1 } };
            toolbar.Add(spacer);
            
            var addToSaveButton = new Button(() => ShowAddToSaveFileMenu())
            {
                text = "Load to Available Quests in Save File",
                style = { marginRight = 10 }
            };
            addToSaveButton.tooltip = "Add this quest to save file's unlocked quests";
            toolbar.Add(addToSaveButton);

            var panButton = new Button { style = { width = 24, height = 24, justifyContent = Justify.Center, alignItems = Align.Center, marginRight = 5 } };
            var panIcon = EditorGUIUtility.IconContent("MoveTool");
            if (panIcon.image != null)
            {
                var panImage = new Image { image = panIcon.image as Texture2D, style = { width = 12, height = 12 } };
                panButton.Add(panImage);
            }
            panButton.tooltip = "Pan";
            toolbar.Add(panButton);

            var selectButton = new Button { style = { width = 24, height = 24, justifyContent = Justify.Center, alignItems = Align.Center, marginRight = 5 } };
            var selectIcon = EditorGUIUtility.IconContent("RectTool");
            if (selectIcon.image != null)
            {
                var selectImage = new Image { image = selectIcon.image as Texture2D, style = { width = 12, height = 12 } };
                selectButton.Add(selectImage);
            }
            selectButton.tooltip = "Select (Box)";
            toolbar.Add(selectButton);
            
            panButton.style.backgroundColor = new Color(0.8f, 0.4f, 0.1f);
            
            m_graphView.SetPanMode(true);
            
            panButton.clicked += () =>
            {
                m_graphView.SetPanMode(true);
                panButton.style.backgroundColor = new Color(0.8f, 0.4f, 0.1f);
                selectButton.style.backgroundColor = new Color(0, 0, 0, 0);
            };
            
            selectButton.clicked += () =>
            {
                m_graphView.SetPanMode(false);
                selectButton.style.backgroundColor = new Color(0.8f, 0.4f, 0.1f);
                panButton.style.backgroundColor = new Color(0, 0, 0, 0);
            };

            var zoomFitButton = new Button(() => m_graphView.ZoomToFit())
            {
                text = "Zoom Fit"
            };
            toolbar.Add(zoomFitButton);

            rootVisualElement.Insert(0, toolbar);
        }

        public void LoadQuest(Gyvr.Mythril2D.Quest quest)
        {
            if (quest == null)
            {
                return;
            }

            m_currentQuest = quest;

            m_currentGraphData = FindOrCreateGraphData(quest);

            titleContent = new GUIContent($"Quest: {quest.title}");

            var questLabel = rootVisualElement.Q<Label>("quest-label");
            if (questLabel != null)
            {
                questLabel.text = $"Quest: {quest.title}";
            }

            m_graphView.LoadGraph(m_currentGraphData);
            
            m_graphView.ZoomToFit();

        }

        private QuestGraphData FindOrCreateGraphData(Gyvr.Mythril2D.Quest quest)
        {
            string questPath = AssetDatabase.GetAssetPath(quest);
            string directory = System.IO.Path.GetDirectoryName(questPath);
            string questFileName = System.IO.Path.GetFileNameWithoutExtension(questPath);
            string graphPath = System.IO.Path.Combine(directory, questFileName + "_graph.asset");

            QuestGraphData graphData = AssetDatabase.LoadAssetAtPath<QuestGraphData>(graphPath);

            if (graphData == null)
            {
                graphData = CreateInstance<QuestGraphData>();
                graphData.questAsset = quest;

                AssetDatabase.CreateAsset(graphData, graphPath);
                AssetDatabase.SaveAssets();

            }
            else
            {
                if (graphData.questAsset != quest)
                {
                    graphData.questAsset = quest;
                    EditorUtility.SetDirty(graphData);
                }
            }

            return graphData;
        }

        private void SaveGraph()
        {
            if (m_currentGraphData == null || m_graphView == null)
            {
                return;
            }

            m_graphView.SaveGraph(m_currentGraphData);
            EditorUtility.SetDirty(m_currentGraphData);
            AssetDatabase.SaveAssets();

        }

        private void OnDestroy()
        {
            if (m_currentGraphData != null && m_graphView != null)
            {
                SaveGraph();
            }
        }

        private void CreateNewQuest()
        {
            string questName = EditorUtility.SaveFilePanelInProject("Create New Quest", "QUEST_NewQuest", "asset", "Enter quest name (without QUEST_ prefix)");
            if (string.IsNullOrEmpty(questName))
                return;

            string assetName = System.IO.Path.GetFileNameWithoutExtension(questName);
            string questDirectory = System.IO.Path.GetDirectoryName(questName);

            if (assetName.StartsWith("QUEST_"))
                assetName = assetName.Substring(6);

            string questFolderPath = System.IO.Path.Combine(questDirectory, assetName);
            if (!AssetDatabase.IsValidFolder(questFolderPath))
            {
                AssetDatabase.CreateFolder(questDirectory, assetName);
            }

            var quest = ScriptableObject.CreateInstance<Gyvr.Mythril2D.Quest>();
            quest.name = $"QUEST_{assetName}";

            var offerDialogue = ScriptableObject.CreateInstance<Gyvr.Mythril2D.DialogueSequence>();
            offerDialogue.name = $"DIAL_{assetName}_Offer";
            
            offerDialogue.options = new Gyvr.Mythril2D.DialogueSequenceOption[2];
            offerDialogue.options[0] = new Gyvr.Mythril2D.DialogueSequenceOption
            {
                name = "Accept",
                message = new Gyvr.Mythril2D.DialogueMessage { type = Gyvr.Mythril2D.EDialogueMessageType.Accept }
            };
            offerDialogue.options[1] = new Gyvr.Mythril2D.DialogueSequenceOption
            {
                name = "Decline",
                message = new Gyvr.Mythril2D.DialogueMessage { type = Gyvr.Mythril2D.EDialogueMessageType.Decline }
            };
            
            var hintDialogue = ScriptableObject.CreateInstance<Gyvr.Mythril2D.DialogueSequence>();
            hintDialogue.name = $"DIAL_{assetName}_Hint";
            
            var completedDialogue = ScriptableObject.CreateInstance<Gyvr.Mythril2D.DialogueSequence>();
            completedDialogue.name = $"DIAL_{assetName}_Completed";

            string questPath = System.IO.Path.Combine(questFolderPath, $"QUEST_{assetName}.asset");
            questPath = AssetDatabase.GenerateUniqueAssetPath(questPath);
            
            AssetDatabase.CreateAsset(quest, questPath);
            
            offerDialogue.name = $"DIAL_{assetName}_Offer";
            AssetDatabase.AddObjectToAsset(offerDialogue, questPath);
            
            hintDialogue.name = $"DIAL_{assetName}_Hint";
            AssetDatabase.AddObjectToAsset(hintDialogue, questPath);
            
            completedDialogue.name = $"DIAL_{assetName}_Completed";
            AssetDatabase.AddObjectToAsset(completedDialogue, questPath);
            
            AssetDatabase.SaveAssets();
            
            var serializedQuest = new SerializedObject(quest);
            serializedQuest.FindProperty("m_questOfferDialogue").objectReferenceValue = offerDialogue;
            serializedQuest.FindProperty("m_questHintDialogue").objectReferenceValue = hintDialogue;
            serializedQuest.FindProperty("m_questCompletedDialogue").objectReferenceValue = completedDialogue;
            serializedQuest.FindProperty("m_title").stringValue = "[New Quest]";
            serializedQuest.ApplyModifiedProperties();
            
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            
            quest = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.Quest>(questPath);

            OpenQuest(quest);
        }
        
        private void ShowAddToSaveFileMenu()
        {
            if (m_currentQuest == null)
            {
                EditorUtility.DisplayDialog("No Quest", "Please load a quest first.", "OK");
                return;
            }
            
            string[] saveFileGuids = AssetDatabase.FindAssets("t:SaveFile");
            
            if (saveFileGuids.Length == 0)
            {
                EditorUtility.DisplayDialog("No Save Files", "No SaveFile assets found in the project.", "OK");
                return;
            }
            
            GenericMenu menu = new GenericMenu();
            var capturedQuest = m_currentQuest;
            
            menu.AddItem(new GUIContent("All Save Files"), false, () => {
                AddQuestToAllSaveFiles();
            });
            menu.AddSeparator("");
            
            foreach (string guid in saveFileGuids)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                var saveFile = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.SaveFile>(path);
                if (saveFile != null)
                {
                    var capturedSaveFile = saveFile;
                    string fileName = System.IO.Path.GetFileNameWithoutExtension(path);
                    bool alreadyUnlocked = IsSaveFileQuestUnlocked(saveFile, capturedQuest);
                    string displayName = alreadyUnlocked ? fileName + " ✓" : fileName;
                    menu.AddItem(new GUIContent(displayName), false, () => {
                        AddQuestToSaveFile(capturedSaveFile);
                    });
                }
            }
            
            menu.ShowAsContext();
        }
        
        private bool IsSaveFileQuestUnlocked(Gyvr.Mythril2D.SaveFile saveFile, Gyvr.Mythril2D.Quest quest)
        {
            var serializedSaveFile = new SerializedObject(saveFile);
            var contentProp = serializedSaveFile.FindProperty("m_content");
            
            if (contentProp == null)
            {
                return false;
            }
            
            var journalProp = contentProp.FindPropertyRelative("journal");
            
            if (journalProp == null)
            {
                return false;
            }
            
            var unlockedQuestsProp = journalProp.FindPropertyRelative("unlockedQuests");
            
            if (unlockedQuestsProp == null)
            {
                return false;
            }
            
            for (int i = 0; i < unlockedQuestsProp.arraySize; i++)
            {
                var questRefProp = unlockedQuestsProp.GetArrayElementAtIndex(i);
                
                if (questRefProp == null)
                {
                    continue;
                }
                
                var instanceProp = questRefProp.FindPropertyRelative("m_instance");
                
                if (instanceProp != null && instanceProp.objectReferenceValue == quest)
                {
                    return true;
                }
            }
            
            return false;
        }
        
        private void AddQuestToSaveFile(Gyvr.Mythril2D.SaveFile saveFile)
        {
            if (m_currentQuest == null)
            {
                EditorUtility.DisplayDialog("Error", "No quest loaded.", "OK");
                return;
            }
            
            if (IsSaveFileQuestUnlocked(saveFile, m_currentQuest))
            {
                EditorUtility.DisplayDialog("Already Added", $"Quest '{m_currentQuest.title}' is already in {saveFile.name}'s unlocked quests.", "OK");
                return;
            }
            
            var serializedSaveFile = new SerializedObject(saveFile);
            var contentProp = serializedSaveFile.FindProperty("m_content");
            
            if (contentProp == null)
            {
                EditorUtility.DisplayDialog("Error", "Save file content property not found.", "OK");
                return;
            }
            
            var journalProp = contentProp.FindPropertyRelative("journal");
            
            if (journalProp == null)
            {
                EditorUtility.DisplayDialog("Error", "Journal property not found in save file.", "OK");
                return;
            }
            
            var unlockedQuestsProp = journalProp.FindPropertyRelative("unlockedQuests");
            
            if (unlockedQuestsProp == null)
            {
                EditorUtility.DisplayDialog("Error", "Unlocked quests property not found.", "OK");
                return;
            }
            
            int newIndex = unlockedQuestsProp.arraySize;
            unlockedQuestsProp.arraySize++;
            
            var newQuestRefProp = unlockedQuestsProp.GetArrayElementAtIndex(newIndex);
            
            var instanceProp = newQuestRefProp.FindPropertyRelative("m_instance");
            
            if (instanceProp != null)
            {
                instanceProp.objectReferenceValue = m_currentQuest;
            }
            
            serializedSaveFile.ApplyModifiedProperties();
            EditorUtility.SetDirty(saveFile);
            AssetDatabase.SaveAssets();
            
            EditorUtility.DisplayDialog("Success", $"Quest '{m_currentQuest.title}' added to {saveFile.name}'s unlocked quests.", "OK");
        }
        
        private void AddQuestToAllSaveFiles()
        {
            string[] saveFileGuids = AssetDatabase.FindAssets("t:SaveFile");
            int addedCount = 0;
            int skippedCount = 0;
            
            foreach (string guid in saveFileGuids)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                var saveFile = AssetDatabase.LoadAssetAtPath<Gyvr.Mythril2D.SaveFile>(path);
                
                if (saveFile != null)
                {
                    if (!IsSaveFileQuestUnlocked(saveFile, m_currentQuest))
                    {
                        var serializedSaveFile = new SerializedObject(saveFile);
                        var contentProp = serializedSaveFile.FindProperty("m_content");
                        
                        if (contentProp == null) continue;
                        
                        var journalProp = contentProp.FindPropertyRelative("journal");
                        
                        if (journalProp == null) continue;
                        
                        var unlockedQuestsProp = journalProp.FindPropertyRelative("unlockedQuests");
                        
                        if (unlockedQuestsProp == null) continue;
                        
                        int newIndex = unlockedQuestsProp.arraySize;
                        unlockedQuestsProp.arraySize++;
                        
                        var newQuestRefProp = unlockedQuestsProp.GetArrayElementAtIndex(newIndex);
                        
                        var instanceProp = newQuestRefProp.FindPropertyRelative("m_instance");
                        
                        if (instanceProp != null)
                        {
                            instanceProp.objectReferenceValue = m_currentQuest;
                        }
                        
                        serializedSaveFile.ApplyModifiedProperties();
                        EditorUtility.SetDirty(saveFile);
                        addedCount++;
                    }
                    else
                    {
                        skippedCount++;
                    }
                }
            }
            
            AssetDatabase.SaveAssets();
            
            string message = $"Quest '{m_currentQuest.title}' added to {addedCount} save file(s).";
            if (skippedCount > 0)
            {
                message += $"\n{skippedCount} save file(s) already had this quest.";
            }
            
            EditorUtility.DisplayDialog("Complete", message, "OK");
        }
        
        public void UpdateWarningIndicator(bool hasWarnings)
        {
            var warningContainer = rootVisualElement.Q<VisualElement>("warning-container");
            if (warningContainer != null)
            {
                warningContainer.style.display = hasWarnings ? DisplayStyle.Flex : DisplayStyle.None;
            }
        }
    }
}
