using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;

/// <summary>
/// Verifies <see cref="UnsTreeService.LoadStructureAsync"/> builds the Enterprise→Cluster→
/// Area→Line→Equipment structure from the config database, including per-equipment tag/
/// virtual-tag counts and the retention of empty clusters.
/// </summary>
[Trait("Category", "Unit")]
public sealed class UnsTreeServiceStructureTests
{
    private static UnsTreeService SeededService()
    {
        var dbName = $"uns-{Guid.NewGuid():N}";
        UnsTreeTestDb.SeedNamed(dbName);
        return new UnsTreeService(UnsTreeTestDb.Factory(dbName));
    }

    /// <summary>Root enterprise "zb" carries both clusters, and MAIN exposes the full
    /// area→line→equipment path with the expected kinds and keys.</summary>
    [Fact]
    public async Task LoadStructure_builds_full_hierarchy()
    {
        var service = SeededService();

        var roots = await service.LoadStructureAsync();

        var enterprise = roots.ShouldHaveSingleItem();
        enterprise.Kind.ShouldBe(UnsNodeKind.Enterprise);
        enterprise.DisplayName.ShouldBe("zb");
        enterprise.Children.Count.ShouldBe(2);
        enterprise.Children.ShouldAllBe(c => c.Kind == UnsNodeKind.Cluster);

        var main = enterprise.Children.Single(c => c.ClusterId == UnsTreeTestDb.PopulatedClusterId);
        main.Kind.ShouldBe(UnsNodeKind.Cluster);

        var area = main.Children.ShouldHaveSingleItem();
        area.Kind.ShouldBe(UnsNodeKind.Area);
        area.Key.ShouldBe("area:AREA-1");
        area.EntityId.ShouldBe("AREA-1");

        var line = area.Children.ShouldHaveSingleItem();
        line.Kind.ShouldBe(UnsNodeKind.Line);
        line.Key.ShouldBe("line:LINE-1");
        line.EntityId.ShouldBe("LINE-1");

        var equipment = line.Children.ShouldHaveSingleItem();
        equipment.Kind.ShouldBe(UnsNodeKind.Equipment);
        equipment.Key.ShouldBe($"eq:{UnsTreeTestDb.SeededEquipmentId}");
        equipment.EntityId.ShouldBe(UnsTreeTestDb.SeededEquipmentId);
        equipment.ClusterId.ShouldBe(UnsTreeTestDb.PopulatedClusterId);
    }

    /// <summary>The seeded equipment node's badge count equals tags + virtual tags. Equipment is a
    /// tree leaf (no expander), so HasLazyChildren is always false.</summary>
    [Fact]
    public async Task LoadStructure_counts_tags_and_vtags_per_equipment()
    {
        var service = SeededService();

        var roots = await service.LoadStructureAsync();

        var equipment = roots
            .Single()
            .Children.Single(c => c.ClusterId == UnsTreeTestDb.PopulatedClusterId)
            .Children.Single()
            .Children.Single()
            .Children.Single();

        // v3: Tag is Raw-only — driver tags no longer bind to equipment, so only the 1 virtual tag
        // contributes to the per-equipment badge count (the 2 seeded raw tags are not counted).
        equipment.ChildCount.ShouldBe(1);
        equipment.HasLazyChildren.ShouldBeFalse();
    }

    /// <summary>An empty cluster (no areas) is still rendered as a Cluster node with no children.</summary>
    [Fact]
    public async Task LoadStructure_includes_empty_clusters()
    {
        var service = SeededService();

        var roots = await service.LoadStructureAsync();

        var siteA = roots
            .Single()
            .Children.Single(c => c.ClusterId == UnsTreeTestDb.EmptyClusterId);

        siteA.Kind.ShouldBe(UnsNodeKind.Cluster);
        siteA.Children.ShouldBeEmpty();
        siteA.ChildCount.ShouldBe(0);
    }
}
