using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;

namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;

/// <summary>
///     v3 Batch 4 (B4-WP1) — the classifier folds the Raw + UNS subtree diff sets into its routing policy:
///     raw/uns adds count as adds (PureAdd), removes as removes (PureRemove), and any Changed raw/uns delta
///     routes to Rebuild (the safe default — WP3's applier owns surgical refinement).
/// </summary>
public sealed class AddressSpaceChangeClassifierDualNamespaceTests
{
    private static readonly RawContainerNode Folder = new("Plant", null, "Plant", RawNodeKind.Folder);

    private static RawTagPlan RawTag(string nodeId) =>
        new("t-1", nodeId, "Plant/Modbus1/PLC-A", "drv-1", "Speed", "Float",
            Writable: false, Alarm: null, ReferencingEquipmentPaths: Array.Empty<string>());

    private static UnsReferenceVariable UnsRef(string nodeId) =>
        new("ref-1", "eq-1", nodeId, "Speed", "Plant/Modbus1/PLC-A/Speed", "Float", Writable: false);

    private static AddressSpacePlan Empty() => new(
        Array.Empty<EquipmentNode>(), Array.Empty<EquipmentNode>(), Array.Empty<AddressSpacePlan.EquipmentDelta>(),
        Array.Empty<DriverInstancePlan>(), Array.Empty<DriverInstancePlan>(), Array.Empty<AddressSpacePlan.DriverDelta>(),
        Array.Empty<ScriptedAlarmPlan>(), Array.Empty<ScriptedAlarmPlan>(), Array.Empty<AddressSpacePlan.AlarmDelta>());

    [Fact]
    public void Added_raw_tag_only_is_PureAdd()
    {
        var plan = Empty() with { AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
    }

    [Fact]
    public void Added_raw_container_only_is_PureAdd()
    {
        var plan = Empty() with { AddedRawContainers = new[] { Folder } };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
    }

    [Fact]
    public void Added_uns_reference_only_is_PureAdd()
    {
        var plan = Empty() with { AddedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
    }

    [Fact]
    public void Removed_raw_tag_only_is_PureRemove()
    {
        var plan = Empty() with { RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove);
    }

    [Fact]
    public void Removed_uns_reference_only_is_PureRemove()
    {
        var plan = Empty() with { RemovedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove);
    }

    [Fact]
    public void Raw_rename_add_and_remove_is_AddRemoveMix()
    {
        var plan = Empty() with
        {
            RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") },
            AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Velocity") },
        };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.AddRemoveMix);
    }

    [Fact]
    public void Changed_raw_tag_is_Rebuild()
    {
        var plan = Empty() with
        {
            ChangedRawTags = new[]
            {
                new AddressSpacePlan.RawTagDelta(
                    RawTag("Plant/Modbus1/PLC-A/Speed"),
                    RawTag("Plant/Modbus1/PLC-A/Speed") with { IsHistorized = true }),
            },
        };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild);
    }

    [Fact]
    public void Uns_reference_repoint_is_Rebuild()
    {
        var plan = Empty() with
        {
            ChangedUnsReferenceVariables = new[]
            {
                new AddressSpacePlan.UnsReferenceDelta(
                    UnsRef("filling/line-1/station-1/Speed"),
                    UnsRef("filling/line-1/station-1/Speed") with { BackingRawPath = "Plant/Modbus1/PLC-A/Velocity" }),
            },
        };
        AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild);
    }
}
