using System.Net;
using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Tests;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Mastery;

public class MasteryCatalogRegistryTests
{
    private static MasteryCatalogRegistry CreateRegistryFromCatalog(MasteryCatalog catalog) =>
        new(catalog);

    [Fact]
    public void TryGetTrack_ShouldReturnTrue_WhenSkillIdExists()
    {
        // Arrange
        var track = new MasteryTrackRow(
            "salvage",
            [new MasteryTierRow(1, 2, [new MasteryBranchRow("scrap_efficiency", null, [])])]);
        var catalog = new MasteryCatalog(
            "/tmp/mastery",
            new Dictionary<string, MasteryTrackRow>(StringComparer.Ordinal) { ["salvage"] = track },
            new Dictionary<string, PerkDefRow>(StringComparer.Ordinal),
            catalogJsonFileCount: 1);
        var registry = CreateRegistryFromCatalog(catalog);
        // Act
        var found = registry.TryGetTrack("salvage", out var result);
        // Assert
        Assert.True(found);
        Assert.NotNull(result);
        Assert.Equal("salvage", result.SkillId);
    }

    [Fact]
    public void TryGetTrack_ShouldReturnFalse_WhenSkillIdUnknown()
    {
        // Arrange
        var catalog = new MasteryCatalog(
            "/tmp/mastery",
            new Dictionary<string, MasteryTrackRow>(StringComparer.Ordinal),
            new Dictionary<string, PerkDefRow>(StringComparer.Ordinal),
            catalogJsonFileCount: 0);
        var registry = CreateRegistryFromCatalog(catalog);
        // Act
        var found = registry.TryGetTrack("not_a_track", out var result);
        // Assert
        Assert.False(found);
        Assert.Null(result);
    }

    [Fact]
    public void TryGetPerk_ShouldReturnTrue_WhenPerkIdExists()
    {
        // Arrange
        var perk = new PerkDefRow("salvage_scrap_efficiency_1", "Scrap Sifter", "gather_yield_modifier");
        var catalog = new MasteryCatalog(
            "/tmp/mastery",
            new Dictionary<string, MasteryTrackRow>(StringComparer.Ordinal),
            new Dictionary<string, PerkDefRow>(StringComparer.Ordinal) { [perk.Id] = perk },
            catalogJsonFileCount: 1);
        var registry = CreateRegistryFromCatalog(catalog);
        // Act
        var found = registry.TryGetPerk("salvage_scrap_efficiency_1", out var result);
        // Assert
        Assert.True(found);
        Assert.NotNull(result);
        Assert.Equal("gather_yield_modifier", result.EffectKind);
    }

    [Fact]
    public void GetTracksInSkillIdOrder_ShouldReturnTracksSortedBySkillId()
    {
        // Arrange
        var refine = new MasteryTrackRow("refine", []);
        var salvage = new MasteryTrackRow("salvage", []);
        var intrusion = new MasteryTrackRow("intrusion", []);
        var catalog = new MasteryCatalog(
            "/tmp/mastery",
            new Dictionary<string, MasteryTrackRow>(StringComparer.Ordinal)
            {
                ["refine"] = refine,
                ["salvage"] = salvage,
                ["intrusion"] = intrusion,
            },
            new Dictionary<string, PerkDefRow>(StringComparer.Ordinal),
            catalogJsonFileCount: 1);
        var registry = CreateRegistryFromCatalog(catalog);
        // Act
        var tracks = registry.GetTracksInSkillIdOrder();
        // Assert
        Assert.Equal(["intrusion", "refine", "salvage"], tracks.Select(t => t.SkillId).ToArray());
    }

    [Fact]
    public void GetPerksInIdOrder_ShouldReturnPerksSortedById()
    {
        // Arrange
        var perks = new Dictionary<string, PerkDefRow>(StringComparer.Ordinal)
        {
            ["z_perk"] = new("z_perk", "Z", null),
            ["a_perk"] = new("a_perk", "A", null),
            ["m_perk"] = new("m_perk", "M", null),
        };
        var catalog = new MasteryCatalog(
            "/tmp/mastery",
            new Dictionary<string, MasteryTrackRow>(StringComparer.Ordinal),
            perks,
            catalogJsonFileCount: 1);
        var registry = CreateRegistryFromCatalog(catalog);
        // Act
        var ordered = registry.GetPerksInIdOrder();
        // Assert
        Assert.Equal(["a_perk", "m_perk", "z_perk"], ordered.Select(p => p.Id).ToArray());
    }

    [Fact]
    public async Task Host_ShouldResolveRegistryFromDi_WhenStartupSucceeds()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        using var client = factory.CreateClient();
        // Act
        var response = await client.GetAsync("/health");
        var registry = factory.Services.GetRequiredService<IMasteryCatalogRegistry>();
        var catalog = factory.Services.GetRequiredService<MasteryCatalog>();
        var foundTrack = registry.TryGetTrack("salvage", out var track);
        var foundPerk = registry.TryGetPerk("salvage_scrap_efficiency_1", out var perk);
        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.Equal(1, catalog.TrackCount);
        Assert.True(catalog.TracksBySkillId.ContainsKey("salvage"));
        Assert.True(foundTrack);
        Assert.NotNull(track);
        Assert.Equal(2, track.Tiers.Count);
        Assert.True(foundPerk);
        Assert.NotNull(perk);
        Assert.Equal("Scrap Sifter", perk.DisplayName);
    }
}
