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

#include <algorithm>
#include <unordered_set>

namespace
{
    void append_descendants(
        const ta::adapters::eft::quests::QuestConditionGraph& graph,
        const std::size_t index,
        ta::adapters::eft::quests::QuestCompletionPlan& plan,
        std::unordered_set<std::string>& completed)
    {
        const auto& nodes = graph.nodes();
        if (index >= nodes.size())
            return;
        for (const std::size_t child_index : nodes[index].children)
        {
            append_descendants(graph, child_index, plan, completed);
            const auto& child = nodes[child_index];
            if (completed.insert(child.id_string).second)
                plan.completed_ids.push_back(child.id);
        }
    }

    void append_root_actions(
        const ta::adapters::eft::quests::QuestConditionNode& node,
        ta::adapters::eft::quests::QuestCompletionPlan& plan)
    {
        if (node.counter && node.target > 0 && node.current < node.target)
            plan.counter_writes.push_back({node.counter, node.target});
        const int missing = (std::max)(0, node.target - node.current);
        if (missing > 0 && !node.target_template_id.empty())
            plan.item_spawns.push_back({node.target_template_id, missing});
    }
}

namespace ta::adapters::eft::quests
{
    QuestCompletionPlan QuestCompletionPlanner::complete_all(const QuestConditionGraph& graph)
    {
        QuestCompletionPlan plan;
        std::unordered_set<std::string> completed;
        const auto& nodes = graph.nodes();
        for (std::size_t index = 0; index < nodes.size(); ++index)
        {
            const QuestConditionNode& node = nodes[index];
            if (!node.counter_root)
                continue;
            append_descendants(graph, index, plan, completed);
            if (completed.insert(node.id_string).second)
                plan.completed_ids.push_back(node.id);
            append_root_actions(node, plan);
        }
        return plan;
    }

    QuestCompletionPlan QuestCompletionPlanner::complete_selected(
        const QuestConditionGraph& graph,
        const std::set<std::string>& selected_ids)
    {
        QuestCompletionPlan plan;
        std::unordered_set<std::string> completed;
        const auto& nodes = graph.nodes();
        for (std::size_t index = 0; index < nodes.size(); ++index)
        {
            const QuestConditionNode& node = nodes[index];
            if (!selected_ids.contains(node.id_string))
                continue;
            append_descendants(graph, index, plan, completed);
            if (completed.insert(node.id_string).second)
                plan.completed_ids.push_back(node.id);
            if (node.counter_root)
                append_root_actions(node, plan);
        }
        return plan;
    }
}
