// Unlock groups as the unit of loot (REQ-LOCK-EXPLICIT, REQ-LOCK-BUILDING,
// REQ-DEF-SCHEMATIC-DROP): buildings can be locked, and one drop can grant a
// building and a module together.

#include <algorithm>
#include <string>
#include <utility>
#include <vector>

#include "catch.hpp"

#include "BuildingType.h"
#include "ConfigLoader.h"
#include "FactionComponent.h"
#include "GameConfig.h"
#include "HealthComponent.h"
#include "SchematicChoiceOption.h"
#include "Simulation.h"
#include "SimulationTestAccess.h"
#include "StationBodyComponent.h"
#include "UnlocksConfig.h"
#include "TestConfig.h"

namespace
{

void killEnemyStations(Simulation& sim)
{
    sim.getAdmin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
        [](entt::entity, StationBodyComponent&, FactionComponent& faction, HealthComponent& health)
        {
            if (faction.isEnemy) { health.hp = 0.0f; }
        });
    sim.tick();
}

// A config whose only unlock group grants the salvager module and the salvage
// bay together, gated to the given station level.
GameConfig configWithSalvageGroup(int stationLevel)
{
    GameConfig cfg = loadTestConfig();
    cfg.unlocks.groups.clear();
    cfg.unlocks.groups.push_back(
        UnlockGroupDef{"salvage_operations", stationLevel, {}, {}, {"salvager"}, {"salvage_bay"}, {}});
    return cfg;
}

} // namespace

TEST_CASE("UnlockGroup: a building granted by a group is locked at game start", "[unlock_group]")
{
    const Simulation sim(configWithSalvageGroup(0));
    CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
    CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
    // A building in no unlock group is available from the start.
    CHECK(sim.isBuildingUnlocked(BuildingType::Miner));
}

TEST_CASE("UnlockGroup: the drop option lists every granted item", "[unlock_group]")
{
    Simulation sim(configWithSalvageGroup(0));
    killEnemyStations(sim);  // level 0
    REQUIRE(sim.hasSchematicChoicesPending());

    const std::vector<SchematicChoiceOption>& options = sim.getPendingSchematicChoices();
    const auto it = std::find_if(options.begin(), options.end(),
        [](const SchematicChoiceOption& o) { return o.unlockGroupId == "salvage_operations"; });
    REQUIRE(it != options.end());

    const bool grantsModule = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
        [](const GrantedSchematic& g) { return g.type == SchematicType::Module && g.id == "salvager"; });
    const bool grantsBuilding = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
        [](const GrantedSchematic& g) { return g.type == SchematicType::Building && g.id == "salvage_bay"; });
    CHECK(grantsModule);
    CHECK(grantsBuilding);
}

TEST_CASE("UnlockGroup: awarding a group unlocks the building and module at once", "[unlock_group]")
{
    Simulation sim(configWithSalvageGroup(0));
    killEnemyStations(sim);  // level 0
    REQUIRE(sim.hasSchematicChoicesPending());

    // The only eligible group is salvage_operations, so option 0 grants it.
    REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
    SimulationTestAccess::applySchematicChoice(sim, 0);

    CHECK(sim.isBuildingUnlocked(BuildingType::SalvageBay));
    CHECK(sim.isModuleSchematicUnlocked("salvager"));
}

TEST_CASE("UnlockGroup: a group above the destroyed station level is not offered", "[unlock_group]")
{
    Simulation sim(configWithSalvageGroup(5));  // eligible only at level >= 5
    killEnemyStations(sim);                      // level 0 push
    // Pool is empty at level 0, so no choice dialog opens at all.
    CHECK_FALSE(sim.hasSchematicChoicesPending());
    CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
}

TEST_CASE("UnlockGroup: reset re-locks a previously awarded building", "[unlock_group]")
{
    Simulation sim(configWithSalvageGroup(0));
    killEnemyStations(sim);
    REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
    SimulationTestAccess::applySchematicChoice(sim, 0);
    REQUIRE(sim.isBuildingUnlocked(BuildingType::SalvageBay));

    sim.reset();

    CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
    CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
}
