#include <algorithm>

#include "catch.hpp"

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

static 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();
}

// Returns the index of the Artifact option in the pending choices, or -1 if absent.
static int findArtifactChoiceIndex(const Simulation& sim)
{
    const auto& choices = sim.getPendingSchematicChoices();
    for (int i = 0; i < static_cast<int>(choices.size()); ++i)
    {
        if (choices[static_cast<std::size_t>(i)].isArtifact)
        {
            return i;
        }
    }
    return -1;
}

// ---------------------------------------------------------------------------
// Config: new artifact fields are parsed correctly (REQ-WIN-ARTIFACT-COUNT)
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: artifact_chance_formula and artifact_win_count are loaded",
          "[artifact_win]")
{
    const GameConfig cfg = loadTestConfig();
    CHECK(cfg.world.artifacts.artifactWinCount == 3);
    // 0.05 * x at x=2 should be 0.1
    CHECK(cfg.world.artifacts.artifactChanceFormula.evaluate(2.0) == Approx(0.1));
}

// ---------------------------------------------------------------------------
// Simulation initial state
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: artifact count is 0 and isWon is false at game start",
          "[artifact_win]")
{
    const Simulation sim(loadTestConfig());
    CHECK(sim.getArtifactCount() == 0);
    CHECK_FALSE(sim.isWon());
}

// ---------------------------------------------------------------------------
// Artifact option presence in schematic choices
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: artifact option appears when chance formula returns 1",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);

    REQUIRE(sim.hasSchematicChoicesPending());
    const auto& choices = sim.getPendingSchematicChoices();

    const long artifactCount = std::count_if(choices.begin(), choices.end(),
        [](const SchematicChoiceOption& opt) { return opt.isArtifact; });
    CHECK(artifactCount == 1);
    // Exactly 2 schematic picks + 1 artifact (or fewer if pool was small)
    CHECK(choices.size() <= 3);
}

TEST_CASE("ArtifactWinCondition: at most 2 schematic options accompany the artifact",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);

    REQUIRE(sim.hasSchematicChoicesPending());
    const auto& choices = sim.getPendingSchematicChoices();

    const long schematicCount = std::count_if(choices.begin(), choices.end(),
        [](const SchematicChoiceOption& opt) { return !opt.isArtifact; });
    CHECK(schematicCount <= 2);
}

TEST_CASE("ArtifactWinCondition: no artifact option when chance formula returns 0",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("0");
    Simulation sim(std::move(cfg));

    for (int i = 0; i < 20; ++i)
    {
        killEnemyStations(sim);
        if (!sim.hasSchematicChoicesPending()) { continue; }

        CHECK(findArtifactChoiceIndex(sim) == -1);
        SimulationTestAccess::applySchematicChoice(sim,0);
    }
}

// ---------------------------------------------------------------------------
// Selecting the artifact option
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: selecting artifact increments artifact count",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);
    REQUIRE(sim.hasSchematicChoicesPending());

    const int index = findArtifactChoiceIndex(sim);
    REQUIRE(index >= 0);

    SimulationTestAccess::applySchematicChoice(sim,index);

    CHECK(sim.getArtifactCount() == 1);
}

TEST_CASE("ArtifactWinCondition: selecting a non-artifact option does not increment artifact count",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);
    REQUIRE(sim.hasSchematicChoicesPending());

    // Pick the first non-artifact choice.
    const auto& choices = sim.getPendingSchematicChoices();
    const auto it = std::find_if(choices.begin(), choices.end(),
        [](const SchematicChoiceOption& opt) { return !opt.isArtifact; });
    REQUIRE(it != choices.end());

    SimulationTestAccess::applySchematicChoice(sim,static_cast<int>(it - choices.begin()));

    CHECK(sim.getArtifactCount() == 0);
}

// ---------------------------------------------------------------------------
// Win condition (REQ-WIN-ARTIFACT-COUNT)
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: isWon becomes true when artifact count reaches win count",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    cfg.world.artifacts.artifactWinCount = 1;
    Simulation sim(std::move(cfg));

    CHECK_FALSE(sim.isWon());

    killEnemyStations(sim);
    REQUIRE(sim.hasSchematicChoicesPending());

    const int index = findArtifactChoiceIndex(sim);
    REQUIRE(index >= 0);
    SimulationTestAccess::applySchematicChoice(sim,index);

    CHECK(sim.isWon());
}

TEST_CASE("ArtifactWinCondition: isWon stays false when artifact count is below win count",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    cfg.world.artifacts.artifactWinCount = 2;
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);
    REQUIRE(sim.hasSchematicChoicesPending());
    SimulationTestAccess::applySchematicChoice(sim,findArtifactChoiceIndex(sim));

    CHECK(sim.getArtifactCount() == 1);
    CHECK_FALSE(sim.isWon());
}

TEST_CASE("ArtifactWinCondition: isWon becomes true after collecting required number of artifacts",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    cfg.world.artifacts.artifactWinCount = 2;
    Simulation sim(std::move(cfg));

    for (int i = 0; i < 2; ++i)
    {
        killEnemyStations(sim);
        REQUIRE(sim.hasSchematicChoicesPending());
        const int index = findArtifactChoiceIndex(sim);
        REQUIRE(index >= 0);
        SimulationTestAccess::applySchematicChoice(sim,index);
    }

    CHECK(sim.getArtifactCount() == 2);
    CHECK(sim.isWon());
}

// ---------------------------------------------------------------------------
// reset() restores initial state (REQ-CFG-RELOAD)
// ---------------------------------------------------------------------------

TEST_CASE("ArtifactWinCondition: reset clears artifact count and win state",
          "[artifact_win]")
{
    GameConfig cfg = loadTestConfig();
    cfg.world.artifacts.artifactChanceFormula = Formula::compile("1");
    cfg.world.artifacts.artifactWinCount = 1;
    Simulation sim(std::move(cfg));

    killEnemyStations(sim);
    REQUIRE(sim.hasSchematicChoicesPending());
    SimulationTestAccess::applySchematicChoice(sim,findArtifactChoiceIndex(sim));
    REQUIRE(sim.isWon());

    sim.reset();

    CHECK(sim.getArtifactCount() == 0);
    CHECK_FALSE(sim.isWon());
}
