//
//  EvacuatePrisonersCommandFactory.cpp
//  eagle0
//

#include "EvacuatePrisonersCommandFactory.hpp"

#include <algorithm>
#include <ranges>
#include <vector>

#include "src/main/cpp/net/eagle0/shardok/library/commands/EvacuatePrisonersCommand.hpp"

namespace shardok {

namespace {
[[nodiscard]] auto IsWarden(const Unit* unit) -> bool {
    return unit->has_attached_hero() &&
           unit->attached_hero().profession_info().profession() ==
                   net::eagle0::shardok::storage::fb::Profession_WARDEN;
}

[[nodiscard]] auto IsHostileToActor(
        const Unit* unit,
        const PlayerId actorPlayerId,
        const vector<PlayerId>& allyPids) -> bool {
    return unit->player_id() != actorPlayerId &&
           !std::ranges::contains(allyPids, unit->player_id());
}

[[nodiscard]] auto CapturedHostileHeroUnitIds(
        const Units* units,
        const PlayerId actorPlayerId,
        const vector<PlayerId>& allyPids) -> std::vector<UnitId> {
    std::vector<UnitId> captiveUnitIds;
    captiveUnitIds.reserve(units->size());
    for (const auto* unit : *units) {
        if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT &&
            unit->has_attached_hero() && IsHostileToActor(unit, actorPlayerId, allyPids)) {
            captiveUnitIds.push_back(unit->unit_id());
        }
    }
    return captiveUnitIds;
}
}  // namespace

EvacuatePrisonersCommandFactory::EvacuatePrisonersCommandFactory(const SettingsGetter& getter)
    : settings(getter) {}

void EvacuatePrisonersCommandFactory::AddAvailableCommands(
        CommandList& commands,
        const CommandFactory::CommandParams& params) const {
    if (!params.unit->can_flee()) return;
    if (!IsWarden(params.unit)) return;
    if (!settings.ActionCostFor(settings.Backing().evacuate_prisoners_action_point_cost())
                 .IsPossible(params.remainingActionPoints))
        return;

    const auto captiveUnitIds =
            CapturedHostileHeroUnitIds(params.units, params.unit->player_id(), params.allyPids);
    if (captiveUnitIds.empty()) return;

    commands.push_back(std::make_shared<EvacuatePrisonersCommand>(
            settings.ActionCostFor(settings.Backing().evacuate_prisoners_action_point_cost()),
            params.unit->player_id(),
            params.unit->unit_id(),
            captiveUnitIds));
}

}  // namespace shardok
