using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using NeonSprawl.Server.Game.Items;
using NeonSprawl.Server.Tests;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Items;

public class ItemDefinitionRegistryTests
{
    private static ItemDefinitionRegistry CreateRegistryFromRows(IReadOnlyDictionary<string, ItemDefRow> byId)
    {
        var catalog = new ItemDefinitionCatalog("/tmp/catalog", byId, catalogJsonFileCount: 1);
        return new ItemDefinitionRegistry(catalog);
    }

    [Fact]
    public void TryGetDefinition_ShouldReturnTrueAndExpectedMetadata_WhenIdExists()
    {
        // Arrange
        var rows = new Dictionary<string, ItemDefRow>(StringComparer.Ordinal)
        {
            ["scrap_metal_bulk"] = new ItemDefRow(
                "scrap_metal_bulk",
                "Scrap Metal (Bulk)",
                "material",
                StackMax: 999,
                InventorySlotKind: "bag",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
        };
        var registry = CreateRegistryFromRows(rows);
        // Act
        var found = registry.TryGetDefinition("scrap_metal_bulk", out var def);
        // Assert
        Assert.True(found);
        Assert.NotNull(def);
        Assert.Equal("material", def.PrototypeRole);
        Assert.Equal("Scrap Metal (Bulk)", def.DisplayName);
        Assert.Equal(999, def.StackMax);
        Assert.Equal("bag", def.InventorySlotKind);
    }

    [Fact]
    public void TryGetDefinition_ShouldReturnFalse_WhenItemIdIsNull()
    {
        // Arrange
        var rows = new Dictionary<string, ItemDefRow>(StringComparer.Ordinal)
        {
            ["scrap_metal_bulk"] = new ItemDefRow(
                "scrap_metal_bulk",
                "Scrap Metal (Bulk)",
                "material",
                StackMax: 999,
                InventorySlotKind: "bag",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
        };
        var registry = CreateRegistryFromRows(rows);
        // Act
        var found = registry.TryGetDefinition(null, out var def);
        // Assert
        Assert.False(found);
        Assert.Null(def);
    }

    [Fact]
    public void TryGetDefinition_ShouldReturnFalse_WhenIdUnknown()
    {
        // Arrange
        var rows = new Dictionary<string, ItemDefRow>(StringComparer.Ordinal)
        {
            ["scrap_metal_bulk"] = new ItemDefRow(
                "scrap_metal_bulk",
                "Scrap Metal (Bulk)",
                "material",
                StackMax: 999,
                InventorySlotKind: "bag",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
        };
        var registry = CreateRegistryFromRows(rows);
        // Act
        var found = registry.TryGetDefinition("not_a_real_item", out var def);
        // Assert
        Assert.False(found);
        Assert.Null(def);
    }

    [Fact]
    public void GetDefinitionsInIdOrder_ShouldListAllRowsOrderedById_WhenMultipleItems()
    {
        // Arrange
        var rows = new Dictionary<string, ItemDefRow>(StringComparer.Ordinal)
        {
            ["survey_drone_kit"] = new ItemDefRow(
                "survey_drone_kit",
                "Survey Drone Kit",
                "utility",
                StackMax: 1,
                InventorySlotKind: "bag",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
            ["scrap_metal_bulk"] = new ItemDefRow(
                "scrap_metal_bulk",
                "Scrap Metal (Bulk)",
                "material",
                StackMax: 999,
                InventorySlotKind: "bag",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
            ["prototype_armor_shell"] = new ItemDefRow(
                "prototype_armor_shell",
                "Prototype Armor Shell",
                "equip_stub",
                StackMax: 1,
                InventorySlotKind: "equipment",
                Rarity: null,
                BindPolicy: null,
                DurabilityMax: null),
        };
        var registry = CreateRegistryFromRows(rows);
        // Act
        var list = registry.GetDefinitionsInIdOrder();
        // Assert
        Assert.Equal(3, list.Count);
        Assert.Equal("prototype_armor_shell", list[0].Id);
        Assert.Equal("scrap_metal_bulk", list[1].Id);
        Assert.Equal("survey_drone_kit", list[2].Id);
    }

    [Fact]
    public void TryGetDefinition_ShouldMatchLoaderCatalog_WhenUsingPrototypeFixture()
    {
        // Arrange
        var root = Directory.CreateTempSubdirectory("neon-sprawl-item-registry-loader-");
        try
        {
            var itemsDir = Path.Combine(root.FullName, "content", "items");
            var schemaDir = Path.Combine(root.FullName, "content", "schemas");
            Directory.CreateDirectory(itemsDir);
            Directory.CreateDirectory(schemaDir);
            var schemaPath = Path.Combine(schemaDir, "item-def.schema.json");
            File.Copy(ItemCatalogTestPaths.DiscoverRepoItemDefSchemaPath(), schemaPath, overwrite: true);
            File.Copy(
                Path.Combine(ItemCatalogTestPaths.DiscoverRepoItemsDirectory(), "prototype_items.json"),
                Path.Combine(itemsDir, "prototype_items.json"),
                overwrite: true);
            var loaded = ItemDefinitionCatalogLoader.Load(itemsDir, schemaPath, NullLogger.Instance);
            var registry = new ItemDefinitionRegistry(loaded);
            // Act
            var ok = registry.TryGetDefinition("prototype_armor_shell", out var equipStub);
            // Assert
            Assert.True(ok);
            Assert.NotNull(equipStub);
            Assert.Equal("equip_stub", equipStub.PrototypeRole);
            Assert.Equal("equipment", equipStub.InventorySlotKind);
            Assert.Equal(1, equipStub.StackMax);
        }
        finally
        {
            try
            {
                Directory.Delete(root.FullName, recursive: true);
            }
            catch (IOException)
            {
                // Best-effort: transient lock or race on some hosts; temp dir is unique per run.
            }
        }
    }

    [Fact]
    public async Task Host_ShouldResolveRegistryFromDi_WhenStartupSucceeds()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        using var client = factory.CreateClient();
        _ = await client.GetAsync("/health");
        // Act
        var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
        var found = registry.TryGetDefinition("scrap_metal_bulk", out var scrap);
        // Assert
        Assert.True(found);
        Assert.NotNull(scrap);
        Assert.Equal("material", scrap.PrototypeRole);
        Assert.Equal(999, scrap.StackMax);
    }
}
