namespace Theriapolis.Core.World.Generation.Stages;

/// <summary>
/// Stage 20 — PoIPlacement
/// Additional Tier 5 PoIs beyond those placed by SettlementPlaceStage.
/// This stage is a lightweight pass to ensure minimum PoI count is met.
/// (SettlementPlaceStage already places Tier 5 — this stage tops up if needed.)
/// </summary>
public sealed class PoIPlacementStage : IWorldGenStage
{
    public string Name => "PoIPlacement";

    private const int W = C.WORLD_WIDTH_TILES;
    private const int H = C.WORLD_HEIGHT_TILES;

    public void Run(WorldGenContext ctx)
    {
        var world = ctx.World;
        var rng   = ctx.Rngs["poi"];

        int existing = world.Settlements.Count(s => s.IsPoi);
        int nextId   = world.Settlements.Count + 1;

        // SettlementPlaceStage may have already placed Tier 5 PoIs
        // This stage ensures the minimum is met and tags the world tiles
        int needed = Math.Max(0, C.SETTLE_TIER5_MIN - existing);

        if (needed == 0)
        {
            ctx.LogMessage($"[PoIPlacement] {existing} PoIs already placed — no top-up needed.");
            return;
        }

        // Find low-habitability land tiles not near settlements
        var candidates = new List<(int x, int y)>();
        for (int y = 0; y < H; y += 4)
        for (int x = 0; x < W; x += 4)
        {
            if (world.Tiles[x, y].Biome == BiomeId.Ocean) continue;
            float hab = world.Habitability?[x, y] ?? 0f;
            if (hab > 0.35f) continue;
            if ((world.Tiles[x, y].Features & FeatureFlags.IsSettlement) != 0) continue;
            if (IsTooCloseToSettlement(world, x, y)) continue;
            candidates.Add((x, y));
        }

        rng.Shuffle(candidates.ToArray().AsSpan());

        int placed = 0;
        foreach (var (x, y) in candidates)
        {
            if (placed >= needed) break;
            if ((world.Tiles[x, y].Features & FeatureFlags.IsPoi) != 0) continue;

            var poiType = PickPoiType(world.Tiles[x, y].Biome, rng);
            var s = new Settlement
            {
                Id      = nextId++,
                Tier    = 5,
                TileX   = x,
                TileY   = y,
                IsPoi   = true,
                PoiType = poiType,
                Name    = DescribePoi(poiType),
            };
            world.Settlements.Add(s);
            world.TileAt(x, y).Features |= FeatureFlags.IsPoi;
            placed++;
        }

        ctx.LogMessage($"[PoIPlacement] Placed {placed} additional PoIs (total: {world.Settlements.Count(s => s.IsPoi)}).");
    }

    private static bool IsTooCloseToSettlement(WorldState world, int x, int y)
    {
        foreach (var s in world.Settlements)
        {
            if (s.IsPoi) continue;
            int dx = x - s.TileX, dy = y - s.TileY;
            if (dx * dx + dy * dy < C.POI_MIN_DIST_FROM_SETTLE * C.POI_MIN_DIST_FROM_SETTLE)
                return true;
        }
        return false;
    }

    private static PoiType PickPoiType(BiomeId biome, Util.SeededRng rng)
    {
        return biome switch
        {
            BiomeId.MountainAlpine or BiomeId.MountainForested => rng.NextBool() ? PoiType.AbandonedMine : PoiType.NaturalCave,
            BiomeId.Tundra or BiomeId.Boreal                   => PoiType.ImperiumRuin,
            BiomeId.Wetland or BiomeId.SubtropicalForest        => rng.NextBool() ? PoiType.NaturalCave : PoiType.CultDen,
            _                                                   => (PoiType)(rng.NextInt(1, 6)),
        };
    }

    private static string DescribePoi(PoiType t) => t switch
    {
        PoiType.ImperiumRuin       => "Imperium Ruin",
        PoiType.AbandonedMine      => "Abandoned Mine",
        PoiType.CultDen            => "Hidden Den",
        PoiType.NaturalCave        => "Natural Cave",
        PoiType.OvergrownSettlement=> "Overgrown Ruins",
        _                          => "Unknown Site",
    };
}
