//
//  AIFleeDecisionCalculator.cpp
//  eagle0
//
//  Handles AI flee decision logic including combat success estimation
//  and flee vs fight evaluation for final round scenarios
//

#include "AIFleeDecisionCalculator.hpp"

#include <iomanip>
#include <iostream>

#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
#include "src/main/cpp/net/eagle0/shardok/ai/AIUnitScoreCalculator.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"

namespace shardok {

namespace {

constexpr double kCertainCombatWinChance = 0.95;

void WritePercent(std::ostream& output, const double value) {
    const auto flags = output.flags();
    const auto precision = output.precision();

    output << std::fixed << std::setprecision(1) << value;

    output.flags(flags);
    output.precision(precision);
}

}  // namespace

auto AIFleeDecisionCalculator::GetFleeCommandIndex(
        const CommandList::const_iterator& fleeCommand,
        const CommandListSPtr& availableCommands) -> size_t {
    return static_cast<size_t>(std::distance(availableCommands->begin(), fleeCommand));
}

auto AIFleeDecisionCalculator::EstimateCombatSuccess(
        PlayerId attackerPlayerId,
        const GameStateW& gameState,
        int maxRounds) -> double {
    if (gameState->status() == nullptr ||
        gameState->status()->state() !=
                net::eagle0::shardok::storage::fb::GameStatus_::State_GAME_RUNNING) {
        return 1.0;  // we're still in set_up so we can't really evaluate
    }

    // Combat success estimation based on unit power, heroes, and capture dynamics
    double attackerPower = 0.0;
    double defenderPower = 0.0;
    int attackerTroops = 0;  // Still track raw troops for special cases
    int defenderTroops = 0;
    int attackerUnits = 0;
    int defenderUnits = 0;
    int attackerHeroes = 0;
    int defenderHeroes = 0;
    bool defenderHasVips = false;

    // Calculate total power and count units/heroes for each side
    for (const auto* unit : *gameState->units()) {
        if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;

        const auto* pi = PlayerInfoForPid(gameState, unit->player_id());
        if (pi == nullptr) continue;

        const int unitTroops = unit->battalion().size();
        const bool hasHero = unit->has_attached_hero();
        const double unitPower = ContextFreeUnitValue(unit);

        if (pi->is_defender()) {
            defenderPower += unitPower;
            defenderTroops += unitTroops;
            defenderUnits++;
            if (hasHero) {
                defenderHeroes++;
                if (unit->attached_hero().is_vip()) { defenderHasVips = true; }
            }
        } else if (unit->player_id() == attackerPlayerId) {
            attackerPower += unitPower;
            attackerTroops += unitTroops;
            attackerUnits++;
            if (hasHero) { attackerHeroes++; }
        }
    }

    const int roundsRemaining = maxRounds - gameState->current_round();

    // Special case: Attacker has no heroes - automatic loss
    if (attackerHeroes == 0) {
        return 0.0;  // Cannot win without heroes
    }

    // Special case: Defender has no heroes - automatic win for attacker
    if (defenderHeroes == 0) {
        return 1.0;  // Guaranteed win
    }

    // Special case: Attacker has no troops (but has heroes)
    if (attackerTroops == 0) {
        // Very difficult to win with heroes alone
        return 0.05;  // Extremely low chance
    }

    // Special case: Defender has no troops but has heroes
    if (defenderTroops == 0) {
        // Defenders with only heroes are vulnerable to capture
        // Only truly difficult if time is extremely limited
        if (roundsRemaining <= 1) {
            // Last round - very hard to capture all heroes
            return 0.3;  // Low but not impossible
        } else if (roundsRemaining <= 2) {
            return 0.6;  // Still achievable
        } else {
            // With 3+ rounds, capturing defenseless heroes is quite feasible
            return 0.85;  // High probability of success
        }
    }

    // Normal case: Both sides have troops
    // Base probability from power ratio (accounts for unit quality, not just quantity)
    const double powerRatio = attackerPower / std::max(1.0, defenderPower);
    double baseProbability = std::min(0.95, std::max(0.05, powerRatio * 0.5));

    // Adjust for time pressure - attackers need to win before time runs out
    if (roundsRemaining <= 1) {
        baseProbability *= 0.6;  // Severe penalty for last round
    } else if (roundsRemaining <= 3) {
        baseProbability *= 0.8;  // Moderate penalty
    }

    // Adjust for unit count (more units = better tactical flexibility)
    const double unitRatio =
            static_cast<double>(attackerUnits) / std::max(1.0, static_cast<double>(defenderUnits));
    if (unitRatio < 0.5) {
        baseProbability *= 0.8;
    } else if (unitRatio > 1.5) {
        baseProbability *= 1.15;
    }

    // Adjust for hero presence
    if (defenderHeroes > attackerHeroes && defenderHasVips) {
        // Defender has more heroes including VIPs - harder to capture
        baseProbability *= 0.85;
    }

    return std::min(0.95, std::max(0.05, baseProbability));
}

auto AIFleeDecisionCalculator::EvaluateFleeVsFight(
        PlayerId playerId,
        const GameStateW& guessedState,
        const CommandListSPtr& availableCommands,
        const CommandList::const_iterator& fleeCommand,
        int maxRounds,
        int /*minimumFleeOddsThreshold*/,
        int /*desperateFleeThreshold*/,
        bool enableDebugLogging) -> FleeDecision {
    // Get flee success odds
    const int fleeSuccessChance = (*fleeCommand)->GetOddsPercentile();

    if (enableDebugLogging) {
        std::cout << "AI FinalRound: Evaluating flee (odds=" << fleeSuccessChance << "%)...\n";
    }

    const double combatWinChance = EstimateCombatSuccess(playerId, guessedState, maxRounds);

    if (combatWinChance < kCertainCombatWinChance) {
        if (enableDebugLogging) {
            std::cout << "AI FinalRound: Combat not certain (";
            WritePercent(std::cout, combatWinChance * 100);
            std::cout << "% < ";
            WritePercent(std::cout, kCertainCombatWinChance * 100);
            std::cout << "%), choosing flee\n";
        }
        return FleeDecision{
                true,
                GetFleeCommandIndex(fleeCommand, availableCommands),
                "Last round combat not certain"};
    }

    if (enableDebugLogging) {
        std::cout << "AI FinalRound: Combat effectively certain (";
        WritePercent(std::cout, combatWinChance * 100);
        std::cout << "%), fighting\n";
    }
    return FleeDecision{
            false,
            0,  // Will be replaced by StandardChooseCommandIndex
            "Combat effectively certain"};
}

auto AIFleeDecisionCalculator::ShouldConsiderFleeing(
        PlayerId attackerPlayerId,
        const GameStateW& guessedState,
        int maxRounds,
        double fleeConsiderationThreshold) -> bool {
    // Get combat success probability
    const double combatSuccessChance =
            EstimateCombatSuccess(attackerPlayerId, guessedState, maxRounds);

    // Consider fleeing if combat success chance is below threshold
    return combatSuccessChance < fleeConsiderationThreshold;
}

}  // namespace shardok
