#pragma once

#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <random>
#include <string>
#include <vector>

#include "BalancingConfig.h"
#include "BeltSystem.h"
#include "FactoryState.h"
#include "EntityAdmin.h"
#include "BuildingId.h"

#include "entt/entity/entity.hpp"
#include "BeamFiredEvent.h"
#include "GameConfig.h"
#include "Tick.h"

class AiSystem;
class BuildingSystem;
class CombatSystem;
class DynamicBodySystem;
class MovementIntentSystem;
class RepairSystem;
class SalvagerSystem;
class ShipSystem;
class DebrisSystem;

struct ArenaStatus
{
    struct Entry
    {
        std::string displayName;
        // Level suffix shown in the widget/inspect display. Set for the HQ and
        // defence stations; empty for ships, which no longer have a level.
        std::optional<int> level;
        int total;
        int surviving;
    };

    struct TeamStatus
    {
        std::string name;
        double threatLevel = 0.0;    // accumulated threat of the team's configured ships
        // Remaining durability of the team's ships and defence stations (HQ
        // excluded). currentEhp is summed live; maxEhp is the fixed full-HP
        // baseline. See getEhpPercentText() for the displayed value.
        double currentEhp = 0.0;
        double maxEhp = 0.0;
        std::vector<Entry> entries;  // HQ first, then ships, then stations

        // Remaining EHP as a whole-number percentage ("NN%"), or "n/a" when the
        // team has no ships or stations (maxEhp == 0).
        std::string getEhpPercentText() const;
    };

    TeamStatus teams[2];
    bool finished = false;
    std::optional<int> winnerTeam;  // 0 or 1 when finished; nullopt while running
    // Game time the fight has lasted (simulated ticks * fixed tick duration).
    // Meaningful once finished; the battle duration shown for completed runs.
    double durationSeconds = 0.0;
};

class ArenaSimulation
{
public:
    ArenaSimulation(const GameConfig& gameConfig,
                    ArenaConfig arenaConfig,
                    unsigned int seed = 0);
    ~ArenaSimulation();

    void run();
    void requestStop();

    void tickOnce();
    std::vector<BeamFiredEvent> drainBeamFiredEvents();

    ArenaStatus getStatus() const;
    bool isFinished() const;
    std::optional<int> getWinnerTeam() const;
    Tick getCurrentTick() const;

    const ArenaConfig& getArenaConfig() const;
    const FactoryState&   getFactoryState() const;
    const ShipSystem& getShips() const;
    const DebrisSystem& getDebrisSystem() const;
    EntityAdmin&       getAdmin();
    const EntityAdmin& getAdmin() const;

private:
    void placeStructures();
    void spawnShips();
    void computeTeamMaxEhp();
    void tick();
    void tickDeaths();
    void updateStatus();

    const GameConfig& m_gameConfig;
    ArenaConfig m_arenaConfig;
    std::mt19937 m_rng;

    Tick m_currentTick;

    EntityAdmin m_admin;
    FactoryState                      m_factoryState;
    BeltSystem m_beltSystem;
    std::unique_ptr<BuildingSystem> m_buildingSystem;
    std::unique_ptr<ShipSystem>     m_shipSystem;
    std::unique_ptr<AiSystem>       m_aiSystem;
    std::unique_ptr<MovementIntentSystem> m_movementIntentSystem;
    std::unique_ptr<DynamicBodySystem>    m_dynamicBodySystem;
    std::unique_ptr<CombatSystem>   m_combatSystem;
    std::unique_ptr<DebrisSystem>    m_debrisSystem;
    std::unique_ptr<SalvagerSystem> m_salvagerSystem;
    std::unique_ptr<RepairSystem>   m_repairSystem;

    entt::entity m_team1HqEntity;
    entt::entity m_team2HqEntity;

    bool m_finished;
    std::optional<int> m_winnerTeam;
    std::atomic<bool> m_stopRequested;

    // Static accumulated threat per team, computed once from the configured roster.
    double m_teamThreat[2] = {0.0, 0.0};

    // Full-HP baseline per team (ships + defence stations, HQ excluded), computed
    // once after spawning; the EHP-percentage denominator.
    double m_teamMaxEhp[2] = {0.0, 0.0};

    std::vector<BeamFiredEvent> m_beamFiredEvents;

    mutable std::mutex m_statusMutex;
    ArenaStatus m_status;
};
