//
// Created by Claude on 3/14/26.
//

#include "BlowBridgeCommandFactory.hpp"

#include <array>

#include "src/main/cpp/net/eagle0/shardok/library/commands/BlowBridgeCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"

namespace shardok {

[[nodiscard]] auto GetBlowBridgeOdds(
        const SettingsGetter& settings,
        double strength,
        double agility,
        double intelligence,
        bool hasForestAccess) -> PercentileRollOdds;

void BlowBridgeCommandFactory::AddAvailableBlowBridgeCommands(
        CommandList& commands,
        const Unit* unit,
        const ActionPoints remainingActionPoints,
        const Coords& position,
        const HexMap* hexMap,
        const Units* units,
        const vector<PlayerId>& allyPids) const {
    const PlayerId playerId = unit->player_id();
    if (!unit->has_attached_hero()) return;
    const auto& hero = unit->attached_hero();

    if (hero.profession_info().profession() !=
        net::eagle0::shardok::storage::fb::Profession_ENGINEER)
        return;

    // Must be on a bridge tile
    const auto* currentTerrain = GetTerrain(hexMap, position);
    if (!currentTerrain->modifier().bridge().present()) return;

    const ActionCost cost =
            settings.ActionCostFor(settings.Backing().blow_bridge_action_point_cost());

    if (!cost.IsPossible(remainingActionPoints)) return;

    const bool nearbyForest = HasForestAccess(position, units, hexMap, allyPids, playerId);

    const auto odds = GetBlowBridgeOdds(
            settings,
            hero.strength(),
            hero.agility(),
            hero.wisdom(),
            nearbyForest);

    for (const Coords& adjCoords : HexMapUtils::GetAdjacentCoords(hexMap, position)) {
        if (IsTraversible(*GetTerrain(hexMap, adjCoords)) && !Occupant(units, adjCoords)) {
            commands.push_back(std::make_shared<BlowBridgeCommand>(
                    settings.ActionCostFor(settings.Backing().blow_bridge_action_point_cost()),
                    unit->player_id(),
                    unit->unit_id(),
                    adjCoords,
                    position,
                    *currentTerrain,
                    nearbyForest,
                    settings.Backing().blow_bridge_agility_xp(),
                    settings.Backing().blow_bridge_strength_xp(),
                    settings.Backing().minimum_knowledge_for_profession(),
                    odds));
        }
    }
}

void BlowBridgeCommandFactory::AddAvailableCommands(
        CommandList& commands,
        const CommandParams& params) const {
    AddAvailableBlowBridgeCommands(
            commands,
            params.unit,
            params.remainingActionPoints,
            params.position,
            params.hexMap,
            params.units,
            params.allyPids);
}

auto GetBlowBridgeOdds(
        const SettingsGetter& settings,
        const double strength,
        const double agility,
        const double intelligence,
        const bool hasForestAccess) -> PercentileRollOdds {
    const int16_t base = settings.Backing().blow_bridge_base_odds();
    constexpr int16_t terrainFactor = 0;
    constexpr int16_t weatherFactor = 0;
    constexpr int16_t windFactor = 0;
    const std::vector stats = {strength, agility, intelligence};
    if (hasForestAccess) {
        const std::array otherFactors = {
                MakeOtherFactor(settings.Backing().blow_bridge_forest_bonus(), "forest nearby")};
        return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats, otherFactors);
    }

    return MakeOdds(base, terrainFactor, weatherFactor, windFactor, stats);
}

}  // namespace shardok
