using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Gathering;
using NeonSprawl.Server.Tests;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Gathering;

public sealed class ResourceNodeInstanceOperationsTests
{
    private const string AlphaId = "prototype_resource_node_alpha";
    private const string DeltaId = "prototype_urban_bulk_delta";

    [Fact]
    public void TryCommitSuccessfulGatherDecrement_OnFreshNode_ShouldLazyInitAndDecrementToNine()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var registry = factory.Services.GetRequiredService<IResourceNodeDefinitionRegistry>();
        var store = factory.Services.GetRequiredService<IResourceNodeInstanceStore>();

        // Act
        var outcome = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);

        // Assert
        Assert.Equal(ResourceNodeInstanceMutationKind.Applied, outcome.Kind);
        Assert.Null(outcome.ReasonCode);
        Assert.NotNull(outcome.Snapshot);
        Assert.Equal(AlphaId, outcome.Snapshot!.Value.InteractableId);
        Assert.Equal(9, outcome.Snapshot.Value.RemainingGathers);
    }

    [Fact]
    public void TryCommitSuccessfulGatherDecrement_RepeatedUntilCapacity_ShouldReturnNodeDepletedWithoutGoingNegative()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var registry = factory.Services.GetRequiredService<IResourceNodeDefinitionRegistry>();
        var store = factory.Services.GetRequiredService<IResourceNodeInstanceStore>();

        // Act — ten successful gathers (10 → 0), then one depletion attempt
        var appliedOutcomes = new ResourceNodeInstanceMutationOutcome[10];
        for (var i = 0; i < appliedOutcomes.Length; i++)
        {
            appliedOutcomes[i] = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);
        }

        var depleted = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);
        store.TryGetRemainingGathers(AlphaId, out var persisted);

        // Assert
        Assert.All(appliedOutcomes, static o =>
        {
            Assert.Equal(ResourceNodeInstanceMutationKind.Applied, o.Kind);
            Assert.True(o.Snapshot!.Value.RemainingGathers >= 0);
        });
        var lastApplied = appliedOutcomes[^1];
        Assert.Equal(0, lastApplied.Snapshot!.Value.RemainingGathers);
        Assert.Equal(ResourceNodeInstanceMutationKind.Denied, depleted.Kind);
        Assert.Equal(ResourceNodeInstanceReasonCodes.NodeDepleted, depleted.ReasonCode);
        Assert.NotNull(depleted.Snapshot);
        Assert.Equal(0, depleted.Snapshot!.Value.RemainingGathers);
        Assert.Equal(0, persisted.RemainingGathers);
    }

    [Fact]
    public void TryCommitSuccessfulGatherDecrement_ForUnknownNode_ShouldDenyWithoutCreatingRow()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var registry = factory.Services.GetRequiredService<IResourceNodeDefinitionRegistry>();
        var store = factory.Services.GetRequiredService<IResourceNodeInstanceStore>();
        const string unknownId = "not_a_real_node_def";

        // Act
        var outcome = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(unknownId, registry, store);
        var rowExists = store.TryGetRemainingGathers(unknownId, out _);

        // Assert
        Assert.Equal(ResourceNodeInstanceMutationKind.Denied, outcome.Kind);
        Assert.Equal(ResourceNodeInstanceReasonCodes.UnknownNode, outcome.ReasonCode);
        Assert.Null(outcome.Snapshot);
        Assert.False(rowExists);
    }

    [Fact]
    public void TryCommitSuccessfulGatherDecrement_ForDeltaNode_ShouldLazyInitAtTen()
    {
        // Arrange
        using var factory = new InMemoryWebApplicationFactory();
        var registry = factory.Services.GetRequiredService<IResourceNodeDefinitionRegistry>();
        var store = factory.Services.GetRequiredService<IResourceNodeInstanceStore>();

        // Act
        var outcome = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(DeltaId, registry, store);

        // Assert
        Assert.Equal(ResourceNodeInstanceMutationKind.Applied, outcome.Kind);
        Assert.Equal(9, outcome.Snapshot!.Value.RemainingGathers);
    }
}
