using System.Linq;
using System.Net;
using System.Net.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Skills;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Skills;

/// <summary>NEO-42: craft/refine completion hook → <c>refine</c> XP via shared NEO-38 grant path (<c>sourceKind: activity</c>).</summary>
public sealed class RefineActivitySkillXpGrantTests
{
    [Fact]
    public async Task GrantOnSuccessfulCraftOrRefine_WhenCatalogAllowsActivity_ShouldIncreaseRefineXp()
    {
        // Arrange — dev player is seeded on the in-memory progression store (NEO-38).
        await using var factory = new InMemoryWebApplicationFactory();
        _ = factory.CreateClient();
        using var scope = factory.Services.CreateScope();
        var registry = scope.ServiceProvider.GetRequiredService<ISkillDefinitionRegistry>();
        var xpStore = scope.ServiceProvider.GetRequiredService<IPlayerSkillProgressionStore>();
        var levelCurve = scope.ServiceProvider.GetRequiredService<ISkillLevelCurve>();
        var perkEngine = scope.ServiceProvider.GetRequiredService<PerkUnlockEngine>();

        // Act
        RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine(
            "dev-local-1",
            registry,
            xpStore,
            levelCurve,
            perkEngine);

        // Assert
        var totals = xpStore.GetXpTotals("dev-local-1");
        Assert.True(totals.TryGetValue("refine", out var xp));
        Assert.Equal(RefineSkillXpConstants.ActivityXpPerCraftRefineCompletion, xp);
    }

    [Fact]
    public async Task GrantOnSuccessfulCraftOrRefine_WhenCatalogAllowsActivity_ShouldMatchSkillProgressionGet()
    {
        // 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);
        using var scope = factory.Services.CreateScope();
        var registry = scope.ServiceProvider.GetRequiredService<ISkillDefinitionRegistry>();
        var xpStore = scope.ServiceProvider.GetRequiredService<IPlayerSkillProgressionStore>();
        var levelCurve = scope.ServiceProvider.GetRequiredService<ISkillLevelCurve>();
        var perkEngine = scope.ServiceProvider.GetRequiredService<PerkUnlockEngine>();

        // Act
        RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine(
            "dev-local-1",
            registry,
            xpStore,
            levelCurve,
            perkEngine);
        var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression");

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

    [Fact]
    public async Task GrantOnSuccessfulCraftOrRefine_WhenRefineDisallowsActivity_ShouldLeaveRefineXpZero()
    {
        // Arrange
        await using var factory = new RefineActivityDeniedRegistryWebApplicationFactory();
        _ = factory.CreateClient();
        using var scope = factory.Services.CreateScope();
        var registry = scope.ServiceProvider.GetRequiredService<ISkillDefinitionRegistry>();
        var xpStore = scope.ServiceProvider.GetRequiredService<IPlayerSkillProgressionStore>();
        var levelCurve = scope.ServiceProvider.GetRequiredService<ISkillLevelCurve>();
        var perkEngine = scope.ServiceProvider.GetRequiredService<PerkUnlockEngine>();

        // Act
        RefineActivitySkillXpGrant.GrantOnSuccessfulCraftOrRefine(
            "dev-local-1",
            registry,
            xpStore,
            levelCurve,
            perkEngine);

        // Assert — deny returns before TryApplyXpDelta; refine row must not appear in stored XP map.
        var totals = xpStore.GetXpTotals("dev-local-1");
        Assert.False(totals.ContainsKey("refine"));
    }
}
