using System.Linq;
using System.Net;
using System.Net.Http.Json;
using NeonSprawl.Server.Game.Interaction;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Skills;
using NeonSprawl.Server.Game.World;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Interaction;

/// <summary>NEO-41: successful <c>resource_node</c> interact grants <c>salvage</c> XP via shared NEO-38 grant path.</summary>
public sealed class InteractionResourceNodeGatherXpTests
{
    [Fact]
    public async Task PostInteract_PrototypeTerminal_ShouldNotIncreaseSalvageXp()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();
        var move = new MoveCommandRequest
        {
            SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
            Target = new PositionVector { X = 0.5, Y = 0.9, Z = 0.5 },
        };
        await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);

        // Act
        var interact = await client.PostAsJsonAsync(
            "/game/players/dev-local-1/interact",
            new InteractionRequest
            {
                SchemaVersion = InteractionRequest.CurrentSchemaVersion,
                InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId,
            });
        var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression");

        // Assert
        Assert.Equal(HttpStatusCode.OK, interact.StatusCode);
        Assert.Equal(HttpStatusCode.OK, snapshot.StatusCode);
        var body = await snapshot.Content.ReadFromJsonAsync<SkillProgressionSnapshotResponse>();
        Assert.NotNull(body);
        var salvage = body!.Skills!.Single(static s => s.Id == "salvage");
        Assert.Equal(0, salvage.Xp);
    }

    [Fact]
    public async Task PostInteract_ResourceNode_WhenInRange_ShouldGrantSalvageActivityXp_ViaProgressionSnapshot()
    {
        // Arrange — node at (12, -6), radius 3; stand at (10, -6) → horizontal distance 2
        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();
        var move = new MoveCommandRequest
        {
            SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
            Target = new PositionVector { X = 10, Y = 0.9, Z = -6 },
        };
        await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);

        // Act
        var interact = await client.PostAsJsonAsync(
            "/game/players/dev-local-1/interact",
            new InteractionRequest
            {
                SchemaVersion = InteractionRequest.CurrentSchemaVersion,
                InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId,
            });
        var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression");

        // Assert
        Assert.Equal(HttpStatusCode.OK, interact.StatusCode);
        var envelope = await interact.Content.ReadFromJsonAsync<InteractionResponse>();
        Assert.NotNull(envelope);
        Assert.True(envelope!.Allowed);

        Assert.Equal(HttpStatusCode.OK, snapshot.StatusCode);
        var body = await snapshot.Content.ReadFromJsonAsync<SkillProgressionSnapshotResponse>();
        Assert.NotNull(body);
        var salvage = body!.Skills!.Single(static s => s.Id == "salvage");
        Assert.Equal(GatherSkillXpConstants.ActivityXpPerResourceNodeGather, salvage.Xp);
    }

    [Fact]
    public async Task PostInteract_ResourceNode_WhenSalvageDisallowsActivity_ShouldStillAllowInteract_AndNotApplyXp()
    {
        // Arrange — stub registry: salvage exists but activity is not in allowedXpSourceKinds (defensive / corrupt-catalog stand-in).
        await using var factory = new SalvageActivityDeniedRegistryWebApplicationFactory();
        var client = factory.CreateClient();
        var move = new MoveCommandRequest
        {
            SchemaVersion = MoveCommandRequest.CurrentSchemaVersion,
            Target = new PositionVector { X = 10, Y = 0.9, Z = -6 },
        };
        await client.PostAsJsonAsync("/game/players/dev-local-1/move", move);

        // Act
        var interact = await client.PostAsJsonAsync(
            "/game/players/dev-local-1/interact",
            new InteractionRequest
            {
                SchemaVersion = InteractionRequest.CurrentSchemaVersion,
                InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId,
            });
        var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression");

        // Assert
        Assert.Equal(HttpStatusCode.OK, interact.StatusCode);
        var envelope = await interact.Content.ReadFromJsonAsync<InteractionResponse>();
        Assert.NotNull(envelope);
        Assert.True(envelope!.Allowed);

        Assert.Equal(HttpStatusCode.OK, snapshot.StatusCode);
        var body = await snapshot.Content.ReadFromJsonAsync<SkillProgressionSnapshotResponse>();
        Assert.NotNull(body);
        var salvage = body!.Skills!.Single(static s => s.Id == "salvage");
        Assert.Equal(0, salvage.Xp);
    }
}
