using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Items;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Items;

public sealed class InMemoryPlayerInventoryStoreTests
{
    [Fact]
    public void TryGetSnapshot_ForSeededDevPlayer_ShouldReturnEmptyInventory()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();

        // Act
        var found = store.TryGetSnapshot("dev-local-1", out var snapshot);

        // Assert
        Assert.True(found);
        Assert.NotNull(snapshot);
        Assert.Equal(PlayerInventorySnapshot.BagSlotCount, snapshot!.BagSlots.Length);
        Assert.Single(snapshot.EquipmentSlots);
        Assert.All(snapshot.BagSlots, static s => Assert.True(s.IsEmpty));
        Assert.All(snapshot.EquipmentSlots, static s => Assert.True(s.IsEmpty));
    }

    [Fact]
    public void TryReplaceSnapshot_ShouldRoundTripOccupiedSlots()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
        store.TryGetSnapshot("dev-local-1", out var before);
        var updated = before!.WithSlots(
            InventoryContainerKind.Bag,
            CloneWithOccupied(before.BagSlots, slotIndex: 3, "scrap_metal_bulk", quantity: 42));

        // Act
        var replaced = store.TryReplaceSnapshot("dev-local-1", updated);
        var readBack = store.TryGetSnapshot("dev-local-1", out var after);

        // Assert
        Assert.True(replaced);
        Assert.True(readBack);
        Assert.Equal("scrap_metal_bulk", after!.BagSlots[3].ItemId);
        Assert.Equal(42, after.BagSlots[3].Quantity);
    }

    [Fact]
    public void TryGetSnapshot_ForUnknownPlayer_ShouldReturnFalse()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();

        // Act
        var found = store.TryGetSnapshot("unknown-player-xyz", out _);

        // Assert
        Assert.False(found);
    }

    private static InventorySlotState[] CloneWithOccupied(
        InventorySlotState[] source,
        int slotIndex,
        string itemId,
        int quantity)
    {
        var copy = new InventorySlotState[source.Length];
        for (var i = 0; i < source.Length; i++)
        {
            var s = source[i];
            copy[i] = i == slotIndex
                ? new InventorySlotState(slotIndex, itemId, quantity)
                : new InventorySlotState(s.SlotIndex, s.ItemId, s.Quantity);
        }

        return copy;
    }
}
