using AssortGenerator.Common.Helpers;
using QuestValidator.Common;
using QuestValidator.Common.Helpers;
using QuestValidator.Helpers;
using QuestValidator.Models;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace GenerateQuestFile
{
    class Program
    {
        /// <summary>
        /// Generate a quests.json file in /output/
        /// Uses every quest from the live quest dump file
        /// If any quests are missing, it will use the quests.json file to fill in the blanks
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            var inputPath = DiskHelpers.CreateWorkingFolders();
            InputFileHelper.SetInputFiles(inputPath);

            // Read in quest files
            var existingQuestData = QuestHelper.GetQuestData();
            var liveQuestData = QuestHelper.GetLiveQuestData();

            // Find the quests that are missing from the live file from existing quest data
            var missingQuests = GetMissingQuestsNotInLiveFile(existingQuestData, liveQuestData);
                
            // Create a list of quests to output
            // Use all quests in live file
            // Use quests from quests.json to fill in missing quests
            var questsToOutputToFile = new Dictionary<string, Quest>();

            // Add live quests to collection to return later
            foreach (var liveQuest in liveQuestData.data)
            {
                questsToOutputToFile.Add(liveQuest._id, liveQuest);
            }

            // Add missing quests from existing quest data to fill in blanks from live data
            foreach (var missingQuest in missingQuests)
            {
                // Going from a pre-12.7.x version has problems, it doesnt have the new quest data format
                CheckAndFixMissingProperties(missingQuest);

                questsToOutputToFile.Add(missingQuest._id, missingQuest);
            }

            if (!questsToOutputToFile.ContainsKey("5e383a6386f77465910ce1f3")) // TextileP1Bear
            {
                // add textileP1Bear
            }

            if (!questsToOutputToFile.ContainsKey("5e4d515e86f77438b2195244")) // TextileP2Bear
            {
                // add TextileP2Bear
            }

            foreach (var quest in questsToOutputToFile)
            {
                AddQuestName(quest);

                var originalQuest = existingQuestData.FirstOrDefault(x => x.Key == quest.Key).Value;
                
                if (originalQuest is null)
                {
                    LoggingHelpers.LogWarning($"Cant check for original start conditions. Unable to find original quest {quest.Key} {QuestHelper.GetQuestNameById(quest.Key)}, skipping.");
                    continue;
                }

                // quest has start conditions, check to ensure they're carried over
                if (originalQuest.conditions.AvailableForStart.Count > 0)
                {
                    AddMissingAvailableForStartConditions(originalQuest, quest);
                }
            }

            // Iterate over quest objects a final time and add hard coded quest requirements if they dont already exist
            foreach (var quest in questsToOutputToFile)
            {
                var questRequirements = QuestRequirements.GetQuestRequirements(quest.Key);
                if (questRequirements is null || questRequirements.Count == 0)
                {
                    LoggingHelpers.LogWarning($"Quest requirement not found for : {quest.Value.QuestName}, skipping.");
                    continue;
                }

                foreach (var requirement in questRequirements)
                {
                    if (!quest.Value.conditions.AvailableForStart.Any(x => x._parent == "Quest"
                            && x._props.target.ToString() == requirement.Quest.Id))
                    {
                        LoggingHelpers.LogSuccess($"{quest.Value.QuestName} needs a prereq of quest {requirement.Quest.Name}, adding.");
                        quest.Value.conditions.AvailableForStart.Add(new AvailableFor
                        {
                            _parent = "Quest",
                            _props = new AvailableForProps
                            {
                                id = "",
                                index = quest.Value.conditions.AvailableForStart.Count,
                                parentId = "",
                                status = new[] { requirement.QuestStatus},
                                target = requirement.Quest.Id
                            }
                        }
                            );
                    }
                    else
                    {
                        if (questRequirements != null)
                        {
                            LoggingHelpers.LogInfo($"{quest.Value.QuestName} already has prereq of quest {requirement.Quest.Name}, skipping.");
                        }
                    }
                }
            }

            JsonWriter.WriteJson(questsToOutputToFile, "output", Directory.GetCurrentDirectory(), "quests");
        }

        private static void CheckAndFixMissingProperties(Quest missingQuest)
        {
            if (missingQuest.description is null)
            {
                missingQuest.description = $"{missingQuest._id} description";
            }

            if (missingQuest.failMessageText is null)
            {
                missingQuest.failMessageText = $"{missingQuest._id} failMessageText";
            }

            if (missingQuest.name is null)
            {
                missingQuest.name = $"{missingQuest._id} name";
            }

            if (missingQuest.note is null)
            {
                missingQuest.note = $"{missingQuest._id} note";
            }


            if (missingQuest.startedMessageText is null)
            {
                missingQuest.startedMessageText = $"{missingQuest._id} startedMessageText";
            }

            if (missingQuest.successMessageText is null)
            {
                missingQuest.successMessageText = $"{missingQuest._id} successMessageText";
            }

            if (missingQuest.templateId is null)
            {
                missingQuest.templateId = $"{missingQuest._id} successMessageText";
            }
        }

        private static void AddMissingAvailableForStartConditions(Quest originalQuest, KeyValuePair<string, Quest> questToUpdate)
        {
            // Iterate over quest requirements in existing quest file
            foreach (var questRequirementToAdd in originalQuest.conditions.AvailableForStart.ToList())
            {
                //Exists already, skip
                if (questToUpdate.Value.conditions.AvailableForStart.Any(x => x._parent == questRequirementToAdd._parent
                && x._props.target?.ToString() == questRequirementToAdd._props.target?.ToString()))
                {
                    continue;
                }

                questToUpdate.Value.conditions.AvailableForStart.Add(questRequirementToAdd);
            }
        }

        /// <summary>
        /// Look up the quests name by guid and add human readable string to quest object
        /// </summary>
        /// <param name="quest"></param>
        private static void AddQuestName(KeyValuePair<string, Quest> quest)
        {
            var questName = QuestHelper.GetQuestNameById(quest.Value._id); // special characters like ", brake the client when it parses it, gotta remove
            var rgx = new Regex("[^a-zA-Z0-9 -]");
            quest.Value.QuestName = rgx.Replace(questName, "");
        }

        /// <summary>
        /// Loop over live quests and use if it exists, otherwise use existing data
        /// </summary>
        private static List<Quest> GetMissingQuestsNotInLiveFile(Dictionary<string, Quest> existingQuests, QuestRoot liveQuestData)
        {
            var missingQuestsToReturn = new List<Quest>();
            foreach (var quest in existingQuests.Values)
            {
                var liveQuest = liveQuestData.data.Find(x => x._id == quest._id);
                if (liveQuest is null)
                {
                    missingQuestsToReturn.Add(quest);
                    LoggingHelpers.LogError($"ERROR Quest {quest._id} {QuestHelper.GetQuestNameById(quest._id)} missing in live file. Will use fallback quests.json");
                }
                else
                {
                    LoggingHelpers.LogSuccess($"SUCCESS Quest {quest._id} {QuestHelper.GetQuestNameById(quest._id)} found in live file.");
                }
            }

            return missingQuestsToReturn;
        }
    }
}