using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests.Alarms;

/// <summary>
///     Contract tests for the <see cref="AlarmConditionInfo"/> record extension added in PR 2.1.
///     Five sub-attribute references (InAlarmRef, PriorityRef, DescAttrNameRef, AckedRef,
///     AckMsgWriteRef) carry the driver-side tag references the server-level alarm-condition
///     service uses to subscribe to live alarm-state attributes and route ack writes.
/// </summary>
public sealed class AlarmConditionInfoTests
{
    /// <summary>Verifies that the legacy three-argument constructor still compiles and defaults refs to null.</summary>
    [Fact]
    public void LegacyThreeArgConstructor_StillCompiles_AndDefaultsRefsToNull()
    {
        var info = new AlarmConditionInfo(
            SourceName: "Tank.HiHi",
            InitialSeverity: AlarmSeverity.High,
            InitialDescription: "High-high alarm");

        info.SourceName.ShouldBe("Tank.HiHi");
        info.InitialSeverity.ShouldBe(AlarmSeverity.High);
        info.InitialDescription.ShouldBe("High-high alarm");
        info.InAlarmRef.ShouldBeNull();
        info.PriorityRef.ShouldBeNull();
        info.DescAttrNameRef.ShouldBeNull();
        info.AckedRef.ShouldBeNull();
        info.AckMsgWriteRef.ShouldBeNull();
    }

    /// <summary>Verifies that the full constructor populates all five sub-attribute references.</summary>
    [Fact]
    public void FullConstructor_PopulatesAllFiveSubAttributeRefs()
    {
        var info = new AlarmConditionInfo(
            SourceName: "Tank1.HiAlarm",
            InitialSeverity: AlarmSeverity.Medium,
            InitialDescription: "Tank level high",
            InAlarmRef: "Tank1.HiAlarm.InAlarm",
            PriorityRef: "Tank1.HiAlarm.Priority",
            DescAttrNameRef: "Tank1.HiAlarm.DescAttrName",
            AckedRef: "Tank1.HiAlarm.Acked",
            AckMsgWriteRef: "Tank1.HiAlarm.AckMsg");

        info.InAlarmRef.ShouldBe("Tank1.HiAlarm.InAlarm");
        info.PriorityRef.ShouldBe("Tank1.HiAlarm.Priority");
        info.DescAttrNameRef.ShouldBe("Tank1.HiAlarm.DescAttrName");
        info.AckedRef.ShouldBe("Tank1.HiAlarm.Acked");
        info.AckMsgWriteRef.ShouldBe("Tank1.HiAlarm.AckMsg");
    }

    /// <summary>Verifies that record equality compares all eight fields.</summary>
    [Fact]
    public void RecordEquality_ComparesAllEightFields()
    {
        var a = new AlarmConditionInfo(
            "T.Alarm", AlarmSeverity.Low, "desc",
            "T.Alarm.InAlarm", "T.Alarm.Priority", "T.Alarm.DescAttrName",
            "T.Alarm.Acked", "T.Alarm.AckMsg");

        var b = new AlarmConditionInfo(
            "T.Alarm", AlarmSeverity.Low, "desc",
            "T.Alarm.InAlarm", "T.Alarm.Priority", "T.Alarm.DescAttrName",
            "T.Alarm.Acked", "T.Alarm.AckMsg");

        a.ShouldBe(b);
    }

    /// <summary>Verifies that records are distinct when any reference differs.</summary>
    [Fact]
    public void RecordEquality_DistinctWhenAnyRefDiffers()
    {
        var baseInfo = new AlarmConditionInfo(
            "T.Alarm", AlarmSeverity.Low, "desc",
            InAlarmRef: "T.Alarm.InAlarm");

        var differingAckRef = baseInfo with { AckedRef = "T.Alarm.Acked" };

        baseInfo.ShouldNotBe(differingAckRef);
    }

    /// <summary>Verifies that with-expressions allow partial updates.</summary>
    [Fact]
    public void WithExpression_AllowsPartialUpdates()
    {
        var legacy = new AlarmConditionInfo("S", AlarmSeverity.Medium, null);

        var enriched = legacy with
        {
            InAlarmRef = "S.InAlarm",
            AckedRef = "S.Acked",
            AckMsgWriteRef = "S.AckMsg",
        };

        enriched.SourceName.ShouldBe("S");
        enriched.InAlarmRef.ShouldBe("S.InAlarm");
        enriched.PriorityRef.ShouldBeNull();
        enriched.DescAttrNameRef.ShouldBeNull();
        enriched.AckedRef.ShouldBe("S.Acked");
        enriched.AckMsgWriteRef.ShouldBe("S.AckMsg");
    }
}
