namespace Theriapolis.Core.Tactical;

/// <summary>
/// Persists per-chunk player-caused modifications (chopped trees, dropped items,
/// emptied bandit camps). Implementations: in-memory (Phase 4 tests + runtime
/// cache), MessagePack-backed (Phase 4 save body — see <see cref="ChunkDelta"/>).
/// </summary>
public interface IChunkDeltaStore
{
    /// <summary>Returns the delta for a chunk, or null if no modifications recorded.</summary>
    ChunkDelta? Get(ChunkCoord cc);

    /// <summary>Records a chunk's delta. Overwrites any existing entry.</summary>
    void Put(ChunkCoord cc, ChunkDelta delta);

    /// <summary>Erases a chunk's delta — the chunk reverts to its deterministic baseline.</summary>
    void Remove(ChunkCoord cc);

    /// <summary>Snapshot of every recorded delta, used by SaveCodec to flush the world.</summary>
    IReadOnlyDictionary<ChunkCoord, ChunkDelta> All { get; }
}

/// <summary>
/// Sparse delta for a single chunk: just the tactical-tile cells that diverge
/// from <see cref="TacticalChunkGen.Generate"/>'s output. Phase 4 stores both
/// the surface and the deco; richer per-tile state arrives later.
/// </summary>
public sealed class ChunkDelta
{
    public List<TileMod> TileMods { get; } = new();

    /// <summary>True if the spawn list has been consumed (all spawns cleared).</summary>
    public bool SpawnsConsumed { get; set; }
}

public readonly struct TileMod
{
    public readonly byte LocalX;
    public readonly byte LocalY;
    public readonly TacticalSurface Surface;
    public readonly TacticalDeco    Deco;
    public readonly byte            Flags;
    public TileMod(int lx, int ly, TacticalSurface s, TacticalDeco d, byte f)
    {
        LocalX = (byte)lx; LocalY = (byte)ly; Surface = s; Deco = d; Flags = f;
    }
}

/// <summary>Trivial in-memory implementation. Used by tests and at runtime before save.</summary>
public sealed class InMemoryChunkDeltaStore : IChunkDeltaStore
{
    private readonly Dictionary<ChunkCoord, ChunkDelta> _store = new();
    public ChunkDelta? Get(ChunkCoord cc) => _store.TryGetValue(cc, out var d) ? d : null;
    public void Put(ChunkCoord cc, ChunkDelta delta) => _store[cc] = delta;
    public void Remove(ChunkCoord cc) => _store.Remove(cc);
    public IReadOnlyDictionary<ChunkCoord, ChunkDelta> All => _store;
}
