using Theriapolis.Core;
using Theriapolis.Core.World;
using Xunit;

namespace Theriapolis.Tests.Worldgen;

/// <summary>
/// DangerZone outputs depend on a fully-generated WorldState; this fixture
/// uses <see cref="WorldCache"/> to amortize the ~30 s pipeline run across
/// the file. Verifies the formula: zones increase with distance from the
/// player-start tier-1 settlement and from roads/settlements.
/// </summary>
public sealed class DangerZoneTests : IClassFixture<WorldCache>
{
    private readonly WorldCache _cache;
    public DangerZoneTests(WorldCache cache) { _cache = cache; }

    [Fact]
    public void Compute_StaysWithinClampedRange()
    {
        var ctx = _cache.Get(seed: 0xCAFEBABEUL);
        for (int i = 0; i < 200; i++)
        {
            int x = (i * 37)  % C.WORLD_WIDTH_TILES;
            int y = (i * 113) % C.WORLD_HEIGHT_TILES;
            int z = DangerZone.Compute(x, y, ctx.World);
            Assert.InRange(z, C.DANGER_ZONE_MIN, C.DANGER_ZONE_MAX);
        }
    }

    [Fact]
    public void Compute_StartTileIsZeroOrLow()
    {
        var ctx = _cache.Get(seed: 0xCAFEBABEUL);
        var (sx, sy) = DangerZone.ResolveStartTile(ctx.World);
        int z = DangerZone.Compute(sx, sy, ctx.World);
        // Player-start should be a low-zone area (zone 0 or 1 at most after
        // biome bonus). Bovid-cities land in grasslands; if the start lands
        // in mountainous biome the bonus pushes us to 1.
        Assert.True(z <= 2, $"Player-start zone unexpectedly high: {z}");
    }

    [Fact]
    public void Compute_FarFromStartIsHigherZone()
    {
        var ctx = _cache.Get(seed: 0xCAFEBABEUL);
        var (sx, sy) = DangerZone.ResolveStartTile(ctx.World);

        int nearZone = DangerZone.Compute(sx, sy, ctx.World);
        // 100 tiles is 2 zones' worth at C.DANGER_DIST_FROM_START_PER_ZONE = 50.
        int farX = System.Math.Min(C.WORLD_WIDTH_TILES - 1, sx + 100);
        int farY = System.Math.Min(C.WORLD_HEIGHT_TILES - 1, sy + 100);
        int farZone = DangerZone.Compute(farX, farY, ctx.World);
        Assert.True(farZone > nearZone,
            $"Far tile zone ({farZone}) should be > near tile zone ({nearZone})");
    }
}
