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

#include "BlowBridgeCommand.hpp"

#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/TileModifierWithCoords.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/unit/Unit.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/ActionResultFlatbufferHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/view_filters/OddsFilter.hpp"
#include "src/main/protobuf/net/eagle0/shardok/common/tile_modifier.pb.h"

namespace shardok {

using net::eagle0::shardok::common::ActionType;

auto BlowBridgeCommand::InternalExecuteWithRoll(
        const GameStateW& currentState,
        const std::shared_ptr<RandomGenerator>& generator,
        const std::optional<int32_t> roll) const -> vector<ActionResult> {
    const auto successRoll = 101 - roll.value_or(generator->Percentile());
    return ExecuteWithRoll(currentState, successRoll);
}

auto BlowBridgeCommand::ExecuteWithRoll(const GameStateW& currentState, double roll) const
        -> vector<ActionResult> {
    const auto* actor = currentState->units()->Get(actorId);
    if (!actor->has_attached_hero()) { throw ActionRequiresHeroException("blow bridge"); }

    auto actorAfter = *actor;
    MutatingSpendActionPoints(&actorAfter, cost);
    MutatingBumpAgilityXp(&actorAfter, agilityXp);
    MutatingBumpStrengthXp(&actorAfter, strengthXp);
    MutatingBumpAllKnowledgeToMinimum(&actorAfter, minimumKnowledgeAfter);

    // Always move engineer to target tile
    actorAfter.mutable_location() = target;

    ActionResult result{};
    result.mutable_player()->set_value(GetPlayerId());
    result.mutable_actor()->set_value(actorAfter.unit_id());
    *result.mutable_target_coords() = ToCoordsProto(target);
    result.mutable_roll()->set_value(roll);
    *result.mutable_odds() = successOdds;

    if (PercentileRollSucceeds(successOdds, roll)) {
        result.set_type(ActionType::BLOW_BRIDGE);

        auto newTileModifier = targetTerrain.modifier();
        SetBridge(newTileModifier, 0.0);
        *result.add_changed_tile_modifiers() = MakeTmc(bridgeCoords, newTileModifier);
    } else {
        result.set_type(ActionType::BLOW_BRIDGE_FAILED);
    }

    AddChangedUnit(result, actorAfter);

    return {result};
}

auto BlowBridgeCommand::GetCommandProto() const -> CommandProto {
    CommandProto proto{};

    proto.set_player(GetPlayerId());
    proto.set_type(net::eagle0::shardok::common::BLOW_BRIDGE_COMMAND);
    proto.mutable_actor()->set_value(actorId);
    *proto.mutable_target() = ToCoordsProto(target);
    proto.mutable_roll_request()->set_roll_type(net::eagle0::shardok::api::ROLL_TYPE_D100);
    proto.mutable_roll_request()->set_command_type(
            net::eagle0::shardok::common::BLOW_BRIDGE_COMMAND);
    proto.mutable_roll_request()->set_acting_unit_id(actorId);
    *proto.mutable_odds() = OddsFilteredForPlayer(successOdds, GetPlayerId());

    return proto;
}

auto BlowBridgeCommand::GetOddsPercentile() const -> int32_t {
    return OddsFilteredForPlayer(successOdds, GetPlayerId()).success_chance();
}

}  // namespace shardok
