namespace Theriapolis.Core.World.Generation.Stages;

/// <summary>
/// Stage 8 — WaterBodyClamp
/// Eliminates all interior water bodies (tiles below sea level that are not
/// connected to the map-edge ocean) by raising them just above sea level.
/// Interior lakes are a later-phase feature; in Phase 1 only ocean water exists.
///
/// Ocean border enforcement is handled earlier in BorderDistortionGenStage
/// (before the wobble pass) so the resulting coastline gets organic treatment.
/// </summary>
public sealed class WaterBodyClampStage : IWorldGenStage
{
    public string Name => "WaterBodyClamp";

    private static readonly (int dx, int dy)[] Dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)];

    public void Run(WorldGenContext ctx)
    {
        int W = C.WORLD_WIDTH_TILES;
        int H = C.WORLD_HEIGHT_TILES;
        var visited = new bool[W, H];
        var queue = new Queue<(int x, int y)>();

        // Flood-fill from map-edge water tiles to mark the ocean body.
        for (int ty = 0; ty < H; ty++)
        for (int tx = 0; tx < W; tx++)
        {
            if (tx != 0 && tx != W - 1 && ty != 0 && ty != H - 1) continue;
            if (ctx.World.Tiles[tx, ty].Elevation >= WorldState.SeaLevel) continue;
            if (visited[tx, ty]) continue;
            visited[tx, ty] = true;
            queue.Enqueue((tx, ty));
        }

        while (queue.Count > 0)
        {
            var (cx, cy) = queue.Dequeue();
            foreach (var (dx, dy) in Dirs)
            {
                int nx = cx + dx, ny = cy + dy;
                if ((uint)nx >= (uint)W || (uint)ny >= (uint)H) continue;
                if (visited[nx, ny]) continue;
                if (ctx.World.Tiles[nx, ny].Elevation >= WorldState.SeaLevel) continue;
                visited[nx, ny] = true;
                queue.Enqueue((nx, ny));
            }
        }

        // Raise all unvisited water tiles above sea level, respecting the
        // macro cell's land elevation range so we don't violate constraints.
        int tilesRaised = 0;
        for (int ty = 0; ty < H; ty++)
        for (int tx = 0; tx < W; tx++)
        {
            if (visited[tx, ty]) continue;
            ref var tile = ref ctx.World.Tiles[tx, ty];
            if (tile.Elevation >= WorldState.SeaLevel) continue;
            var cell = ctx.World.MacroGrid![tile.MacroX, tile.MacroY];
            float landFloor = MathF.Max(cell.ElevationFloor, WorldState.SeaLevel);
            tile.Elevation = landFloor + 0.02f;
            tilesRaised++;
        }

        ctx.World.StageHashes["WaterBodyClamp"] = ctx.World.HashElevation();
        ctx.LogMessage($"[WaterBodyClamp] Filled {tilesRaised} interior water tiles");
    }
}
