using UnityEngine;
using UnityEditor;
using Gyvr.Mythril2D;
using System.Collections.Generic;
using System.Reflection;
using System;

namespace M2DVisualQuestEditor
{
    public static class NPCInteractionManager
    {
        public static bool AddOrUpdateConditionalInteraction(
            GameObject npcPrefab, 
            QuestTask task, 
            string interactionTypeName, 
            DialogueSequence dialogueForSequence = null,
            string conditionType = "IsQuestTaskActive",
            Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "",
            Quest interactionQuest = null)
        {
            if (npcPrefab == null)
            {
                return false;
            }

            string prefabPath = AssetDatabase.GetAssetPath(npcPrefab);
            if (string.IsNullOrEmpty(prefabPath))
            {
                return false;
            }

            using (var editScope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
            {
                var prefabRoot = editScope.prefabContentsRoot;
                
                var entity = prefabRoot.GetComponent<Entity>();
                if (entity == null)
                {
                    return false;
                }

                var serializedEntity = new SerializedObject(entity);
                var interactionProperty = serializedEntity.FindProperty("m_interaction");
                
                if (interactionProperty == null)
                {
                    return false;
                }

                bool success = AddOrUpdateTypedInteraction(serializedEntity, interactionProperty, task, interactionTypeName, dialogueForSequence, conditionType, conditionQuest, conditionQuestState, conditionGameFlag, interactionQuest);
                
                if (success)
                {
                    serializedEntity.ApplyModifiedProperties();
                }
                
                return success;
            }
        }
        
        public static bool AddQuestTaskInteraction(
            GameObject npcPrefab, 
            QuestTask task, 
            string interactionTypeName, 
            DialogueSequence dialogueForSequence = null,
            string conditionType = "IsQuestTaskActive",
            Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "")
        {
            if (npcPrefab == null)
            {
                return false;
            }

            string prefabPath = AssetDatabase.GetAssetPath(npcPrefab);
            if (string.IsNullOrEmpty(prefabPath))
            {
                return false;
            }

            using (var editScope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
            {
                var prefabRoot = editScope.prefabContentsRoot;
                
                var entity = prefabRoot.GetComponent<Entity>();
                if (entity == null)
                {
                    return false;
                }

                var serializedEntity = new SerializedObject(entity);
                var interactionProperty = serializedEntity.FindProperty("m_interaction");
                
                if (interactionProperty == null)
                {
                    return false;
                }

                bool success = AddTypedInteractionToEntity(serializedEntity, interactionProperty, task, interactionTypeName, dialogueForSequence, conditionType, conditionQuest, conditionQuestState, conditionGameFlag);
                
                if (success)
                {
                    serializedEntity.ApplyModifiedProperties();
                }
                
                return success;
            }
        }
        
        public static bool AddQuestTaskDialogue(GameObject npcPrefab, QuestTask task, DialogueSequence dialogue)
        {
            if (npcPrefab == null || task == null || dialogue == null)
            {
                return false;
            }

            string prefabPath = AssetDatabase.GetAssetPath(npcPrefab);
            if (string.IsNullOrEmpty(prefabPath))
            {
                return false;
            }

            using (var editScope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
            {
                var prefabRoot = editScope.prefabContentsRoot;
                
                var entity = prefabRoot.GetComponent<Entity>();
                if (entity == null)
                {
                    return false;
                }

                var serializedEntity = new SerializedObject(entity);
                var interactionProperty = serializedEntity.FindProperty("m_interaction");
                
                if (interactionProperty == null)
                {
                    return false;
                }

                bool success = AddInteractionToEntity(serializedEntity, interactionProperty, task, dialogue);
                
                if (success)
                {
                    serializedEntity.ApplyModifiedProperties();
                }
                
                return success;
            }
        }

        public static bool RemoveQuestTaskDialogue(GameObject npcPrefab, QuestTask task, DialogueSequence dialogue)
        {
            if (npcPrefab == null)
            {
                return false;
            }

            string prefabPath = AssetDatabase.GetAssetPath(npcPrefab);
            if (string.IsNullOrEmpty(prefabPath))
            {
                return false;
            }

            using (var editScope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
            {
                var prefabRoot = editScope.prefabContentsRoot;
                var entity = prefabRoot.GetComponent<Entity>();
                
                if (entity == null) return false;

                var serializedEntity = new SerializedObject(entity);
                bool success = RemoveInteractionFromEntity(serializedEntity, task, dialogue);
                
                if (success)
                {
                    serializedEntity.ApplyModifiedProperties();
                }
                
                return success;
            }
        }

        public static bool HasQuestTaskDialogue(GameObject npcPrefab, QuestTask task, DialogueSequence dialogue)
        {
            if (npcPrefab == null) return false;

            var entity = npcPrefab.GetComponent<Entity>();
            if (entity == null) return false;

            var interactionField = typeof(Entity).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
            if (interactionField == null) return false;

            var interaction = interactionField.GetValue(entity);
            return FindMatchingInteraction(interaction, task, dialogue);
        }

        public static List<(QuestTask task, DialogueSequence dialogue)> GetQuestTaskDialogues(GameObject npcPrefab)
        {
            var result = new List<(QuestTask, DialogueSequence)>();
            
            if (npcPrefab == null) return result;

            var entity = npcPrefab.GetComponent<Entity>();
            if (entity == null) return result;

            var interactionField = typeof(Entity).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
            if (interactionField == null) return result;

            var interaction = interactionField.GetValue(entity);
            CollectQuestTaskDialogues(interaction, result);
            
            return result;
        }

        private static bool AddInteractionToEntity(SerializedObject serializedEntity, SerializedProperty interactionProperty, QuestTask task, DialogueSequence dialogue)
        {
            
            var currentInteraction = interactionProperty.managedReferenceValue;
            
            var conditionalInteraction = new ConditionalInteraction();
            
            var condition = new IsQuestTaskActive();
            var conditionTaskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
            conditionTaskField?.SetValue(condition, task);
            
            var dialogueInteraction = new DialogueInteraction();
            var dialogueField = typeof(DialogueInteraction).GetField("m_sequence", BindingFlags.NonPublic | BindingFlags.Instance);
            dialogueField?.SetValue(dialogueInteraction, dialogue);
            
            var conditionField = typeof(ConditionalInteraction).GetField("m_condition", BindingFlags.NonPublic | BindingFlags.Instance);
            var interactionFieldOnConditional = typeof(ConditionalInteraction).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
            conditionField?.SetValue(conditionalInteraction, condition);
            interactionFieldOnConditional?.SetValue(conditionalInteraction, dialogueInteraction);

            if (currentInteraction == null)
            {
                interactionProperty.managedReferenceValue = conditionalInteraction;
                return true;
            }
            else if (currentInteraction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var existingArray = interactionsField?.GetValue(sequential) as IInteraction[];
                
                var newArray = new IInteraction[(existingArray?.Length ?? 0) + 1];
                
                newArray[0] = conditionalInteraction;
                if (existingArray != null)
                {
                    for (int i = 0; i < existingArray.Length; i++)
                    {
                        newArray[i + 1] = existingArray[i];
                    }
                }
                
                interactionsField?.SetValue(sequential, newArray);
                
                interactionProperty.managedReferenceValue = sequential;
                return true;
            }
            else
            {
                var newSequential = new SequentialInteraction();
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                
                interactionsField?.SetValue(newSequential, new IInteraction[] { conditionalInteraction, (IInteraction)currentInteraction });
                
                interactionProperty.managedReferenceValue = newSequential;
                return true;
            }
        }

        private static bool AddOrUpdateTypedInteraction(
            SerializedObject serializedEntity, 
            SerializedProperty interactionProperty, 
            QuestTask task, 
            string interactionTypeName, 
            DialogueSequence dialogueForSequence,
            string conditionType = "IsQuestTaskActive",
            Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "",
            Quest interactionQuest = null)
        {
            RemoveMatchingConditionalInteraction(interactionProperty, task, conditionType, conditionQuest, conditionQuestState, conditionGameFlag);
            
            return AddTypedInteractionToEntityInternal(serializedEntity, interactionProperty, task, interactionTypeName, dialogueForSequence, conditionType, conditionQuest, conditionQuestState, conditionGameFlag, interactionQuest);
        }
        
        private static void RemoveMatchingConditionalInteraction(
            SerializedProperty interactionProperty, 
            QuestTask task,
            string conditionType,
            Quest conditionQuest,
            string conditionQuestState,
            string conditionGameFlag)
        {
            var currentInteraction = interactionProperty.managedReferenceValue;
            if (currentInteraction == null) return;

            if (currentInteraction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var existingArray = interactionsField?.GetValue(sequential) as IInteraction[];
                
                if (existingArray == null) return;

                var newList = new List<IInteraction>();
                
                foreach (var interaction in existingArray)
                {
                    if (interaction is ConditionalInteraction conditional && ConditionMatchesCriteria(conditional, task, conditionType, conditionQuest, conditionQuestState, conditionGameFlag))
                    {
                        continue;
                    }
                    newList.Add(interaction);
                }
                
                if (newList.Count == 0)
                {
                    interactionProperty.managedReferenceValue = null;
                }
                else if (newList.Count == 1)
                {
                    interactionProperty.managedReferenceValue = newList[0];
                }
                else
                {
                    interactionsField?.SetValue(sequential, newList.ToArray());
                    interactionProperty.managedReferenceValue = sequential;
                }
            }
            else if (currentInteraction is ConditionalInteraction conditional && ConditionMatchesCriteria(conditional, task, conditionType, conditionQuest, conditionQuestState, conditionGameFlag))
            {
                interactionProperty.managedReferenceValue = null;
            }
        }

        private static bool ConditionMatchesCriteria(
            ConditionalInteraction conditional, 
            QuestTask task,
            string conditionType,
            Quest conditionQuest,
            string conditionQuestState,
            string conditionGameFlag)
        {
            var conditionField = typeof(ConditionalInteraction).GetField("m_condition", BindingFlags.NonPublic | BindingFlags.Instance);
            var condition = conditionField?.GetValue(conditional);
            if (condition == null) return false;
            
            string actualConditionType = condition.GetType().Name;
            if (actualConditionType != conditionType) return false;

            switch (conditionType)
            {
                case "IsQuestTaskActive":
                    if (condition is IsQuestTaskActive taskActive)
                    {
                        var taskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                        var existingTask = taskField?.GetValue(taskActive) as QuestTask;
                        return existingTask == task;
                    }
                    break;
                    
                case "IsQuestInState":
                    if (condition is IsQuestInState questState)
                    {
                        var questField = typeof(IsQuestInState).GetField("m_quest", BindingFlags.NonPublic | BindingFlags.Instance);
                        var stateField = typeof(IsQuestInState).GetField("m_state", BindingFlags.NonPublic | BindingFlags.Instance);
                        var existingQuest = questField?.GetValue(questState) as Quest;
                        var existingState = stateField?.GetValue(questState);
                        return existingQuest == conditionQuest && existingState?.ToString() == conditionQuestState;
                    }
                    break;
                    
                case "IsQuestTaskInState":
                    var taskInStateTaskField = condition.GetType().GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                    var existingTaskInState = taskInStateTaskField?.GetValue(condition) as QuestTask;
                    return existingTaskInState == task;
                    
                case "IsGameFlagSet":
                    if (condition is IsGameFlagSet gameFlagCond)
                    {
                        var gameFlagField = typeof(IsGameFlagSet).GetField("m_gameFlag", BindingFlags.NonPublic | BindingFlags.Instance);
                        var existingFlag = gameFlagField?.GetValue(gameFlagCond) as string;
                        return existingFlag == conditionGameFlag;
                    }
                    break;
            }
            
            return false;
        }

        private static bool AddTypedInteractionToEntity(
            SerializedObject serializedEntity, 
            SerializedProperty interactionProperty, 
            QuestTask task, 
            string interactionTypeName, 
            DialogueSequence dialogueForSequence,
            string conditionType = "IsQuestTaskActive",
            Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "")
        {
            return AddTypedInteractionToEntityInternal(serializedEntity, interactionProperty, task, interactionTypeName, dialogueForSequence, conditionType, conditionQuest, conditionQuestState, conditionGameFlag, null);
        }
        
        private static bool AddTypedInteractionToEntityInternal(
            SerializedObject serializedEntity, 
            SerializedProperty interactionProperty, 
            QuestTask task, 
            string interactionTypeName, 
            DialogueSequence dialogueForSequence,
            string conditionType = "IsQuestTaskActive",
            Quest conditionQuest = null,
            string conditionQuestState = "Active",
            string conditionGameFlag = "",
            Quest interactionQuest = null)
        {
            var currentInteraction = interactionProperty.managedReferenceValue;
            
            var conditionalInteraction = new ConditionalInteraction();
            
            ICondition condition = CreateCondition(conditionType, task, conditionQuest, conditionQuestState, conditionGameFlag);
            if (condition == null) return false;
            
            IInteraction innerInteraction = null;
            
            var mythril2dAssembly = typeof(IInteraction).Assembly;
            var interactionType = mythril2dAssembly.GetType($"Gyvr.Mythril2D.{interactionTypeName}");
            
            if (interactionType != null && typeof(IInteraction).IsAssignableFrom(interactionType))
            {
                innerInteraction = Activator.CreateInstance(interactionType) as IInteraction;
                
                if (innerInteraction != null)
                {
                    switch (interactionTypeName)
                    {
                        case "DialogueInteraction":
                            if (dialogueForSequence != null)
                            {
                                var seqField = interactionType.GetField("m_sequence", BindingFlags.NonPublic | BindingFlags.Instance);
                                seqField?.SetValue(innerInteraction, dialogueForSequence);
                            }
                            break;
                        case "CommandInteraction":
                            break;
                        case "SequentialInteraction":
                            break;
                        case "QuestInteraction":
                            if (interactionQuest != null)
                            {
                                var questFieldRef = interactionType.GetField("m_quest", BindingFlags.NonPublic | BindingFlags.Instance);
                                questFieldRef?.SetValue(innerInteraction, interactionQuest);
                            }
                            break;
                    }
                }
            }
            else
            {
                var dialogueInteraction = new DialogueInteraction();
                innerInteraction = dialogueInteraction;
            }
            
            var conditionField = typeof(ConditionalInteraction).GetField("m_condition", BindingFlags.NonPublic | BindingFlags.Instance);
            var interactionFieldOnConditional = typeof(ConditionalInteraction).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
            conditionField?.SetValue(conditionalInteraction, condition);
            interactionFieldOnConditional?.SetValue(conditionalInteraction, innerInteraction);

            if (currentInteraction == null)
            {
                interactionProperty.managedReferenceValue = conditionalInteraction;
                return true;
            }
            else if (currentInteraction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var existingArray = interactionsField?.GetValue(sequential) as IInteraction[];
                
                var newArray = new IInteraction[(existingArray?.Length ?? 0) + 1];
                
                newArray[0] = conditionalInteraction;
                if (existingArray != null)
                {
                    for (int i = 0; i < existingArray.Length; i++)
                    {
                        newArray[i + 1] = existingArray[i];
                    }
                }
                
                interactionsField?.SetValue(sequential, newArray);
                
                interactionProperty.managedReferenceValue = sequential;
                return true;
            }
            else
            {
                var newSequential = new SequentialInteraction();
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                
                interactionsField?.SetValue(newSequential, new IInteraction[] { conditionalInteraction, (IInteraction)currentInteraction });
                
                interactionProperty.managedReferenceValue = newSequential;
                return true;
            }
        }

        private static ICondition CreateCondition(string conditionType, QuestTask task, Quest quest, string questState, string gameFlag)
        {
            switch (conditionType)
            {
                case "IsQuestTaskActive":
                    var taskActiveCondition = new IsQuestTaskActive();
                    var taskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                    taskField?.SetValue(taskActiveCondition, task);
                    return taskActiveCondition;
                    
                case "IsQuestInState":
                    var questInStateCondition = new IsQuestInState();
                    var questField = typeof(IsQuestInState).GetField("m_quest", BindingFlags.NonPublic | BindingFlags.Instance);
                    var stateField = typeof(IsQuestInState).GetField("m_state", BindingFlags.NonPublic | BindingFlags.Instance);
                    questField?.SetValue(questInStateCondition, quest);
                    if (Enum.TryParse<EQuestState>(questState, out var parsedState))
                    {
                        stateField?.SetValue(questInStateCondition, parsedState);
                    }
                    return questInStateCondition;
                    
                case "IsQuestTaskInState":
                    var taskInStateCondition = Activator.CreateInstance(typeof(IsQuestTaskActive).Assembly.GetType("Gyvr.Mythril2D.IsQuestTaskInState")) as ICondition;
                    if (taskInStateCondition != null)
                    {
                        var taskInStateTaskField = taskInStateCondition.GetType().GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                        taskInStateTaskField?.SetValue(taskInStateCondition, task);
                    }
                    return taskInStateCondition;
                    
                case "IsGameFlagSet":
                    var gameFlagCondition = new IsGameFlagSet();
                    var gameFlagField = typeof(IsGameFlagSet).GetField("m_gameFlag", BindingFlags.NonPublic | BindingFlags.Instance);
                    gameFlagField?.SetValue(gameFlagCondition, gameFlag);
                    return gameFlagCondition;
                    
                default:
                    var defaultCondition = new IsQuestTaskActive();
                    var defaultTaskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                    defaultTaskField?.SetValue(defaultCondition, task);
                    return defaultCondition;
            }
        }

        private static bool RemoveInteractionFromEntity(SerializedObject serializedEntity, QuestTask task, DialogueSequence dialogue)
        {
            var interactionProperty = serializedEntity.FindProperty("m_interaction");
            if (interactionProperty == null) return false;

            var currentInteraction = interactionProperty.managedReferenceValue;
            if (currentInteraction == null) return false;

            if (currentInteraction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var existingArray = interactionsField?.GetValue(sequential) as IInteraction[];
                
                if (existingArray == null) return false;

                var newList = new List<IInteraction>();
                bool found = false;
                
                foreach (var interaction in existingArray)
                {
                    if (interaction is ConditionalInteraction conditional && IsMatchingConditional(conditional, task, dialogue))
                    {
                        found = true;
                        continue; // Skip this one
                    }
                    newList.Add(interaction);
                }
                
                if (found)
                {
                    if (newList.Count == 0)
                    {
                        interactionProperty.managedReferenceValue = null;
                    }
                    else if (newList.Count == 1)
                    {
                        interactionProperty.managedReferenceValue = newList[0];
                    }
                    else
                    {
                        interactionsField?.SetValue(sequential, newList.ToArray());
                        interactionProperty.managedReferenceValue = sequential;
                    }
                    return true;
                }
            }
            else if (currentInteraction is ConditionalInteraction conditional && IsMatchingConditional(conditional, task, dialogue))
            {
                interactionProperty.managedReferenceValue = null;
                return true;
            }

            return false;
        }

        private static bool IsMatchingConditional(ConditionalInteraction conditional, QuestTask task, DialogueSequence dialogue)
        {
            var conditionField = typeof(ConditionalInteraction).GetField("m_condition", BindingFlags.NonPublic | BindingFlags.Instance);
            var interactionField = typeof(ConditionalInteraction).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
            
            var condition = conditionField?.GetValue(conditional);
            var interaction = interactionField?.GetValue(conditional);

            if (condition is IsQuestTaskActive taskCondition && interaction is DialogueInteraction dialogueInt)
            {
                var taskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                var dialogueField = typeof(DialogueInteraction).GetField("m_sequence", BindingFlags.NonPublic | BindingFlags.Instance);
                
                var conditionTask = taskField?.GetValue(taskCondition) as QuestTask;
                var conditionDialogue = dialogueField?.GetValue(dialogueInt) as DialogueSequence;
                
                return conditionTask == task && conditionDialogue == dialogue;
            }

            return false;
        }

        private static bool FindMatchingInteraction(object interaction, QuestTask task, DialogueSequence dialogue)
        {
            if (interaction == null) return false;

            if (interaction is ConditionalInteraction conditional)
            {
                return IsMatchingConditional(conditional, task, dialogue);
            }
            else if (interaction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var array = interactionsField?.GetValue(sequential) as IInteraction[];
                
                if (array != null)
                {
                    foreach (var item in array)
                    {
                        if (FindMatchingInteraction(item, task, dialogue))
                            return true;
                    }
                }
            }

            return false;
        }

        private static void CollectQuestTaskDialogues(object interaction, List<(QuestTask, DialogueSequence)> result)
        {
            if (interaction == null) return;

            if (interaction is ConditionalInteraction conditional)
            {
                var conditionField = typeof(ConditionalInteraction).GetField("m_condition", BindingFlags.NonPublic | BindingFlags.Instance);
                var interactionField = typeof(ConditionalInteraction).GetField("m_interaction", BindingFlags.NonPublic | BindingFlags.Instance);
                
                var condition = conditionField?.GetValue(conditional);
                var childInteraction = interactionField?.GetValue(conditional);

                if (condition is IsQuestTaskActive taskCondition && childInteraction is DialogueInteraction dialogueInt)
                {
                    var taskField = typeof(IsQuestTaskActive).GetField("m_task", BindingFlags.NonPublic | BindingFlags.Instance);
                    var dialogueField = typeof(DialogueInteraction).GetField("m_sequence", BindingFlags.NonPublic | BindingFlags.Instance);
                    
                    var task = taskField?.GetValue(taskCondition) as QuestTask;
                    var dialogue = dialogueField?.GetValue(dialogueInt) as DialogueSequence;
                    
                    if (task != null && dialogue != null)
                    {
                        result.Add((task, dialogue));
                    }
                }
            }
            else if (interaction is SequentialInteraction sequential)
            {
                var interactionsField = typeof(SequentialInteraction).GetField("m_interactions", BindingFlags.NonPublic | BindingFlags.Instance);
                var array = interactionsField?.GetValue(sequential) as IInteraction[];
                
                if (array != null)
                {
                    foreach (var item in array)
                    {
                        CollectQuestTaskDialogues(item, result);
                    }
                }
            }
        }
    }
}
