#include "AiSystem.h"
#include "FactoryQueries.h"

#include <limits>

#include "GameConfig.h"

#include "AdvanceBehavior.h"
#include "AttackBehavior.h"
#include "BehaviorKind.h"
#include "DeliverScrapBehavior.h"
#include "EntityAdmin.h"
#include "RallyBehavior.h"
#include "RepairBehavior.h"
#include "RetreatBehavior.h"
#include "SalvageScrapBehavior.h"
#include "SelectedBehaviorComponent.h"
#include "StandbyBehavior.h"
#include "tracing.h"

namespace
{
    // Records a behavior's score for its owning ship, keeping the highest seen.
    // Considered high-priority first, so strict '>' breaks ties toward priority.
    template <typename Behavior>
    void consider(EntityAdmin& admin, BehaviorKind kind)
    {
        admin.forEach<Behavior, SelectedBehaviorComponent>(
            [kind](entt::entity /*e*/, const Behavior& behavior,
                   SelectedBehaviorComponent& selected)
            {
                if (behavior.score > selected.bestScore)
                {
                    selected.bestScore = behavior.score;
                    selected.winner    = kind;
                }
            });
    }
}

AiSystem::AiSystem(const GameConfig& config)
    : m_attackEvaluator(config.world.targeting)
{
}

void AiSystem::tick(EntityAdmin& admin, const FactoryState& state)
{
    TRACE();

    // Phase 1: evaluators score behaviors and set their target data.
    m_advanceEvaluator.evaluate(admin);
    m_standbyEvaluator.evaluate(admin);
    m_rallyEvaluator.evaluate(admin);
    m_retreatEvaluator.evaluate(admin);
    m_attackEvaluator.evaluate(admin);
    m_repairEvaluator.evaluate(admin);
    m_salvageScrapEvaluator.evaluate(admin);
    m_deliverScrapEvaluator.evaluate(admin, state);

    // Phase 2: pick the highest-scoring behavior per ship.
    selectWinningBehaviors(admin);

    // Phase 3: executors run for the winning behavior.
    m_advanceExecutor.execute(admin);
    m_standbyExecutor.execute(admin);
    m_rallyExecutor.execute(admin);
    m_retreatExecutor.execute(admin);
    m_attackExecutor.execute(admin);
    m_repairExecutor.execute(admin);
    m_salvageScrapExecutor.execute(admin);
    m_deliverScrapExecutor.execute(admin, state);
}

void AiSystem::selectWinningBehaviors(EntityAdmin& admin)
{
    TRACE();
    admin.forEach<SelectedBehaviorComponent>(
        [](entt::entity /*e*/, SelectedBehaviorComponent& selected)
        {
            selected.winner    = BehaviorKind::None;
            selected.bestScore = std::numeric_limits<float>::lowest();
        });

    // Highest priority first so ties resolve toward the more urgent behavior.
    consider<RetreatBehavior>(admin, BehaviorKind::Retreat);
    consider<AttackBehavior>(admin, BehaviorKind::Attack);
    consider<RepairBehavior>(admin, BehaviorKind::Repair);
    consider<SalvageScrapBehavior>(admin, BehaviorKind::SalvageScrap);
    consider<DeliverScrapBehavior>(admin, BehaviorKind::DeliverScrap);
    consider<RallyBehavior>(admin, BehaviorKind::Rally);
    consider<StandbyBehavior>(admin, BehaviorKind::Standby);
    consider<AdvanceBehavior>(admin, BehaviorKind::Advance);
}
