using Theriapolis.Core;
using Xunit;

namespace Theriapolis.Tests.Dungeons;

/// <summary>
/// Phase 7 M0 — schema integrity tests for the Phase 7 constants. These
/// guard against silent regressions in:
///   - <see cref="C.SAVE_SCHEMA_VERSION"/> (must == 8 at Phase 7 ship)
///   - The 4 new RNG sub-streams (must be unique vs every existing stream)
///   - Dungeon size bands (must be a coherent ladder)
///   - Movement-cost multipliers (must be ≥ 1.0; squeezing must dominate)
/// </summary>
public sealed class Phase7ConstantsTests
{
    [Fact]
    public void SaveSchemaVersion_IsEight()
    {
        Assert.Equal(8, C.SAVE_SCHEMA_VERSION);
    }

    [Fact]
    public void DungeonRngSubStreams_AreDistinctFromAllExistingStreams()
    {
        // Collect every named ulong RNG sub-stream by reflection. Each
        // must be unique — a collision means two independent streams share
        // a seed, breaking the dice contract.
        var fields = typeof(C).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
            .Where(f => f.IsLiteral && f.FieldType == typeof(ulong))
            .ToArray();
        var seen = new Dictionary<ulong, string>();
        foreach (var f in fields)
        {
            ulong value = (ulong)f.GetRawConstantValue()!;
            if (seen.TryGetValue(value, out var prior))
                Assert.Fail($"RNG sub-stream collision: {f.Name} == {prior} ({value:X})");
            seen[value] = f.Name;
        }
        // Belt-and-braces: assert the four Phase 7 streams exist.
        Assert.Contains(C.RNG_DUNGEON_LAYOUT,   seen.Keys);
        Assert.Contains(C.RNG_ROOM_PICK,        seen.Keys);
        Assert.Contains(C.RNG_DUNGEON_POPULATE, seen.Keys);
        Assert.Contains(C.RNG_DUNGEON_LOOT,     seen.Keys);
    }

    [Fact]
    public void DungeonSizeBands_FormCoherentLadder()
    {
        Assert.True(C.DUNGEON_SMALL_ROOMS_MIN <= C.DUNGEON_SMALL_ROOMS_MAX);
        Assert.True(C.DUNGEON_MED_ROOMS_MIN   <= C.DUNGEON_MED_ROOMS_MAX);
        Assert.True(C.DUNGEON_LARGE_ROOMS_MIN <= C.DUNGEON_LARGE_ROOMS_MAX);
        // Ladders don't overlap — a small dungeon's max < medium's min.
        Assert.True(C.DUNGEON_SMALL_ROOMS_MAX < C.DUNGEON_MED_ROOMS_MIN);
        Assert.True(C.DUNGEON_MED_ROOMS_MAX   < C.DUNGEON_LARGE_ROOMS_MIN);
    }

    [Fact]
    public void MovementCostMultipliers_AreOrdered()
    {
        Assert.True(C.MOVE_COST_MISMATCH_LIGHT >= 1.0f,
            "Mismatch must never give a speed bonus.");
        Assert.True(C.MOVE_COST_MISMATCH_LIGHT < C.MOVE_COST_MISMATCH_MED);
        Assert.True(C.MOVE_COST_MISMATCH_MED   < C.MOVE_COST_MISMATCH_HEAVY);
    }

    [Fact]
    public void LockAndTrapDcs_AreOrdered()
    {
        Assert.True(C.LOCK_DC_TRIVIAL < C.LOCK_DC_EASY);
        Assert.True(C.LOCK_DC_EASY    < C.LOCK_DC_MEDIUM);
        Assert.True(C.LOCK_DC_MEDIUM  < C.LOCK_DC_HARD);
        Assert.True(C.TRAP_DC_TRIVIAL < C.TRAP_DC_EASY);
        Assert.True(C.TRAP_DC_EASY    < C.TRAP_DC_MEDIUM);
    }
}
