using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
using ZB.MOM.WW.OtOpcUa.Server.Phase7;

namespace ZB.MOM.WW.OtOpcUa.Server.Tests.Phase7;

/// <summary>
///     Task #246 — covers the deterministic mapping inside <see cref="Phase7Composer"/>
///     that turns <see cref="EquipmentNamespaceContent"/> into the path → fullRef map
///     <see cref="DriverFeed.PathToFullRef"/> consumes. Pure function; no DI / DB needed.
/// </summary>
[Trait("Category", "Unit")]
public sealed class Phase7ComposerMappingTests
{
    private static UnsArea Area(string id, string name) =>
        new() { UnsAreaId = id, ClusterId = "c", Name = name, GenerationId = 1 };

    private static UnsLine Line(string id, string areaId, string name) =>
        new() { UnsLineId = id, UnsAreaId = areaId, Name = name, GenerationId = 1 };

    private static Equipment Eq(string id, string lineId, string name) => new()
    {
        EquipmentRowId = Guid.NewGuid(), GenerationId = 1, EquipmentId = id,
        EquipmentUuid = Guid.NewGuid(), DriverInstanceId = "drv",
        UnsLineId = lineId, Name = name, MachineCode = "m",
    };

    private static Tag T(string id, string name, string fullRef, string equipmentId) => new()
    {
        TagRowId = Guid.NewGuid(), GenerationId = 1, TagId = id,
        DriverInstanceId = "drv", EquipmentId = equipmentId,
        Name = name, DataType = "Float32",
        AccessLevel = TagAccessLevel.Read, TagConfig = fullRef,
    };

    [Fact]
    public void Maps_tag_to_UNS_path_walker_emits()
    {
        var content = new EquipmentNamespaceContent(
            Areas: [Area("a1", "warsaw")],
            Lines: [Line("l1", "a1", "oven-line")],
            Equipment: [Eq("e1", "l1", "oven-3")],
            Tags: [T("t1", "Temp", "DR.Temp", "e1")]);

        var map = Phase7Composer.MapPathsToFullRefs(content);

        map.ShouldContainKeyAndValue("/warsaw/oven-line/oven-3/Temp", "DR.Temp");
    }

    [Fact]
    public void Skips_tag_with_null_EquipmentId()
    {
        var content = new EquipmentNamespaceContent(
            [Area("a1", "warsaw")], [Line("l1", "a1", "ol")], [Eq("e1", "l1", "ov")],
            [T("t1", "Bare", "DR.Bare", null!)]);  // SystemPlatform-style orphan

        Phase7Composer.MapPathsToFullRefs(content).ShouldBeEmpty();
    }

    [Fact]
    public void Skips_tag_pointing_at_unknown_Equipment()
    {
        var content = new EquipmentNamespaceContent(
            [Area("a1", "warsaw")], [Line("l1", "a1", "ol")], [Eq("e1", "l1", "ov")],
            [T("t1", "Lost", "DR.Lost", "e-missing")]);

        Phase7Composer.MapPathsToFullRefs(content).ShouldBeEmpty();
    }

    [Fact]
    public void Maps_multiple_tags_under_same_equipment_distinctly()
    {
        var content = new EquipmentNamespaceContent(
            [Area("a1", "site")], [Line("l1", "a1", "line1")], [Eq("e1", "l1", "cell")],
            [T("t1", "Temp", "DR.T", "e1"), T("t2", "Pressure", "DR.P", "e1")]);

        var map = Phase7Composer.MapPathsToFullRefs(content);

        map.Count.ShouldBe(2);
        map["/site/line1/cell/Temp"].ShouldBe("DR.T");
        map["/site/line1/cell/Pressure"].ShouldBe("DR.P");
    }

    [Fact]
    public void Empty_content_yields_empty_map()
    {
        Phase7Composer.MapPathsToFullRefs(new EquipmentNamespaceContent([], [], [], []))
            .ShouldBeEmpty();
    }
}
