#include "adapters/eft/quests/EftQuestService.h"

#include <set>
#include <string>
#include <utility>

#include "adapters/eft/quests/conditions/QuestConditionService.h"
#include "adapters/eft/quests/QuestRuntimeReader.h"
#include "adapters/eft/bindings/QuestAccess.h"
#include "adapters/il2cpp/ManagedRuntime.h"
#include "adapters/il2cpp/Il2CppObjectAccess.h"
#include "adapters/il2cpp/Il2CppString.h"
#include "foundation/diagnostics/BuildOptions.h"
#include "foundation/diagnostics/Logger.h"

namespace ta::adapters::eft::quests
{
    namespace
    {
        void skip_task(ta::adapters::il2cpp::Il2CppObjectInstance& quest_controller,
                       ta::adapters::il2cpp::Il2CppObjectInstance& quest_template,
                       const std::string& id)
        {
            Il2CppString* quest_id = ta::adapters::eft::quests::bindings::QuestAccess::quest_id(quest_template);
            if (!quest_id)
            {
                ta::foundation::logger::warn("GAME", "quest skip failed | reason=missing quest id id=" + id);
                return;
            }

            const auto result = ta::adapters::il2cpp::ManagedRuntime::invoke<Il2CppObject*>(
                quest_controller.get_instance(),
                "SendQuestCompleteToBackend",
                ta::adapters::il2cpp::LookupPolicy::IncludeBaseClasses,
                quest_id);
            if (!result)
            {
                ta::foundation::logger::warn("GAME", "quest skip failed | reason=backend method unavailable id=" + id);
                return;
            }

            if (ta::foundation::diagnostics::build_options::verbose)
                ta::foundation::logger::info("GAME", "quest backend completion requested | id=" + id);
        }

        void complete_quest(ta::adapters::il2cpp::Il2CppObjectInstance& quest_controller,
                            ta::adapters::il2cpp::Il2CppObjectInstance& quest,
                            ta::adapters::il2cpp::Il2CppObjectInstance& quest_template,
                            ta::adapters::il2cpp::Il2CppObjectInstance main_player,
                            const std::string& id)
        {
            QuestConditionService::complete_all_conditions(quest, main_player);
            QuestConditionService::set_quest_status_completed(quest, id);
            skip_task(quest_controller, quest_template, id);
        }

        void complete_conditions(ta::adapters::il2cpp::Il2CppObjectInstance& quest,
                                 ta::adapters::il2cpp::Il2CppObjectInstance main_player,
                                 const std::set<std::string>& condition_ids)
        {
            QuestConditionService::complete_selected_conditions(
                quest,
                main_player,
                condition_ids);
        }
    }

    ta::features::quests::dependencies::QuestProcessResult process_quests(
        ta::adapters::il2cpp::Il2CppObjectInstance main_player,
        const ta::features::quests::contracts::PendingQuestCommands& commands)
    {
        using ta::features::quests::contracts::QuestStatus;

        ta::features::quests::dependencies::QuestProcessResult result;
        result.visited = QuestRuntimeReader::visit_quests(
            main_player,
            [&](QuestRuntimeQuest& runtime_quest)
            {
                if (runtime_quest.status != QuestStatus::Started)
                {
                    if (commands.contains(runtime_quest.id))
                        result.completed_ids.insert(runtime_quest.id);
                    return;
                }

                if (commands.complete_quest.contains(runtime_quest.id))
                {
                    complete_quest(
                        runtime_quest.quest_controller,
                        runtime_quest.quest,
                        runtime_quest.quest_template,
                        main_player,
                        runtime_quest.id);
                    result.completed_ids.insert(runtime_quest.id);
                }
                else if (const auto selected = commands.complete_conditions.find(runtime_quest.id);
                         selected != commands.complete_conditions.end())
                {
                    complete_conditions(runtime_quest.quest, main_player, selected->second);
                    result.completed_ids.insert(runtime_quest.id);
                }
                else if (commands.skip_task.contains(runtime_quest.id))
                {
                    skip_task(
                        runtime_quest.quest_controller,
                        runtime_quest.quest_template,
                        runtime_quest.id);
                    result.completed_ids.insert(runtime_quest.id);
                }

                auto entry = QuestRuntimeReader::make_quest_entry(runtime_quest);
                entry.conditions = QuestConditionService::collect_condition_entries(
                    runtime_quest.quest,
                    main_player);
                result.snapshot.push_back(std::move(entry));
            });

        return result;
    }
}
