using NeonSprawl.Server.Game.PositionState;

namespace NeonSprawl.Server.Game.Items;

/// <summary>
/// Maps <c>GET</c>/<c>POST /game/players/{{id}}/inventory</c> — read snapshot and add/remove mutations (NEO-55).
/// </summary>
public static class PlayerInventoryApi
{
    public static WebApplication MapPlayerInventoryApi(this WebApplication app)
    {
        app.MapGet(
            "/game/players/{id}/inventory",
            (string id, IPositionStateStore positions, IPlayerInventoryStore store) =>
            {
                var trimmedId = id.Trim();
                if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
                {
                    return Results.NotFound();
                }

                if (!store.TryGetSnapshot(trimmedId, out var snapshot))
                {
                    return Results.NotFound();
                }

                return Results.Json(MapSnapshot(trimmedId, snapshot));
            });

        app.MapPost(
            "/game/players/{id}/inventory",
            (string id, PlayerInventoryMutationRequest? body, IPositionStateStore positions,
                IItemDefinitionRegistry registry, IPlayerInventoryStore store) =>
            {
                if (body is null || body.SchemaVersion != PlayerInventoryMutationRequest.CurrentSchemaVersion)
                {
                    return Results.BadRequest();
                }

                if (!TryParseMutationKind(body.MutationKind, out var isAdd))
                {
                    return Results.BadRequest();
                }

                var trimmedId = id.Trim();
                if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
                {
                    return Results.NotFound();
                }

                var outcome = isAdd
                    ? PlayerInventoryOperations.TryAddStack(trimmedId, body.ItemId, body.Quantity, registry, store)
                    : PlayerInventoryOperations.TryRemoveStack(trimmedId, body.ItemId, body.Quantity, registry, store);

                return outcome.Kind switch
                {
                    PlayerInventoryMutationKind.StoreMissing => Results.NotFound(),
                    PlayerInventoryMutationKind.Denied => Results.Json(
                        BuildMutationResponse(trimmedId, applied: false, outcome.ReasonCode, outcome.Snapshot, store)),
                    PlayerInventoryMutationKind.Applied => Results.Json(
                        BuildMutationResponse(trimmedId, applied: true, reasonCode: null, outcome.Snapshot, store)),
                    _ => throw new InvalidOperationException($"Unexpected inventory mutation outcome: {outcome.Kind}"),
                };
            });

        return app;
    }

    internal static PlayerInventorySnapshotResponse MapSnapshot(string playerId, PlayerInventorySnapshot snapshot) =>
        new()
        {
            PlayerId = playerId,
            SchemaVersion = PlayerInventorySnapshotResponse.CurrentSchemaVersion,
            BagSlots = MapSlots(snapshot.BagSlots),
            EquipmentSlots = MapSlots(snapshot.EquipmentSlots),
        };

    private static IReadOnlyList<InventorySlotStateJson> MapSlots(InventorySlotState[] slots)
    {
        var rows = new InventorySlotStateJson[slots.Length];
        for (var i = 0; i < slots.Length; i++)
        {
            var slot = slots[i];
            rows[i] = slot.IsEmpty
                ? new InventorySlotStateJson { SlotIndex = slot.SlotIndex, Quantity = 0 }
                : new InventorySlotStateJson
                {
                    SlotIndex = slot.SlotIndex,
                    ItemId = slot.ItemId,
                    Quantity = slot.Quantity,
                };
        }

        return rows;
    }

    private static PlayerInventoryMutationResponse BuildMutationResponse(
        string playerId,
        bool applied,
        string? reasonCode,
        PlayerInventorySnapshot? snapshot,
        IPlayerInventoryStore store)
    {
        if (snapshot is null && !store.TryGetSnapshot(playerId, out snapshot))
        {
            snapshot = PlayerInventorySnapshot.Empty();
        }

        return new PlayerInventoryMutationResponse
        {
            Applied = applied,
            ReasonCode = reasonCode,
            Inventory = MapSnapshot(playerId, snapshot!),
        };
    }

    private static bool TryParseMutationKind(string? raw, out bool isAdd)
    {
        if (string.Equals(raw, "add", StringComparison.OrdinalIgnoreCase))
        {
            isAdd = true;
            return true;
        }

        if (string.Equals(raw, "remove", StringComparison.OrdinalIgnoreCase))
        {
            isAdd = false;
            return true;
        }

        isAdd = false;
        return false;
    }
}
