using ZB.MOM.WW.MxGateway.Contracts.Proto;
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
using ZB.MOM.WW.MxGateway.Server.Dashboard;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;

/// <summary>
///     Unit tests for the pure projection/formatting helpers behind the
///     dashboard Browse and Alarms tabs.
/// </summary>
public sealed class DashboardBrowseAndAlarmModelTests
{
    [Fact]
    public void BuildTree_LinksChildrenToParents_AndPromotesOrphansToRoots()
    {
        GalaxyObject area = new() { GobjectId = 1, BrowseName = "AreaA", IsArea = true, ParentGobjectId = 0 };
        GalaxyObject child = new() { GobjectId = 2, BrowseName = "Pump01", ParentGobjectId = 1 };
        GalaxyObject orphan = new() { GobjectId = 3, BrowseName = "Lost", ParentGobjectId = 99 };

        IReadOnlyList<DashboardBrowseNode> roots = DashboardBrowseTreeBuilder.Build([area, child, orphan]);

        // The area and the orphan (its parent id is absent) are both roots.
        Assert.Equal(2, roots.Count);
        DashboardBrowseNode areaNode = Assert.Single(roots, node => node.Object.GobjectId == 1);
        Assert.Single(areaNode.Children);
        Assert.Equal(2, areaNode.Children[0].Object.GobjectId);
        Assert.Contains(roots, node => node.Object.GobjectId == 3);
    }

    [Fact]
    public void BuildTree_SortsAreasBeforeObjects()
    {
        GalaxyObject instance = new() { GobjectId = 1, BrowseName = "Zeta", IsArea = false };
        GalaxyObject areaB = new() { GobjectId = 2, BrowseName = "Beta", IsArea = true };

        IReadOnlyList<DashboardBrowseNode> roots = DashboardBrowseTreeBuilder.Build([instance, areaB]);

        Assert.Equal(2, roots.Count);
        Assert.True(roots[0].IsArea);
        Assert.Equal("Beta", roots[0].DisplayName);
        Assert.False(roots[1].IsArea);
    }

    [Theory]
    [InlineData(true, "true")]
    [InlineData(false, "false")]
    public void FormatValue_FormatsBooleans(bool input, string expected)
    {
        MxValue value = new() { DataType = MxDataType.Boolean, BoolValue = input };
        Assert.Equal(expected, DashboardMxValueFormatter.FormatValue(value));
    }

    [Fact]
    public void FormatValue_FormatsNumbersAndStrings()
    {
        Assert.Equal("42", DashboardMxValueFormatter.FormatValue(new MxValue { Int32Value = 42 }));
        Assert.Equal("hello", DashboardMxValueFormatter.FormatValue(new MxValue { StringValue = "hello" }));
    }

    [Fact]
    public void FormatValue_HandlesNullPayloadAndNullReference()
    {
        Assert.Equal("-", DashboardMxValueFormatter.FormatValue(null));
        Assert.Equal("(null)", DashboardMxValueFormatter.FormatValue(new MxValue { IsNull = true }));
    }

    [Fact]
    public void TagValue_FromSuccessfulReadResult_MarksGoodQuality()
    {
        BulkReadResult result = new()
        {
            TagAddress = "Galaxy!Area.Tag",
            WasSuccessful = true,
            Quality = 192,
            Value = new MxValue { DataType = MxDataType.Double, DoubleValue = 1.5 },
        };

        DashboardTagValue value = DashboardTagValue.FromBulkReadResult(result);

        Assert.True(value.Ok);
        Assert.True(value.QualityGood);
        Assert.Equal("1.5", value.ValueText);
        Assert.Null(value.Error);
    }

    [Fact]
    public void TagValue_FromFailedReadResult_CarriesError()
    {
        BulkReadResult result = new()
        {
            TagAddress = "Galaxy!Area.Bad",
            WasSuccessful = false,
            Quality = 0,
            ErrorMessage = "invalid handle",
        };

        DashboardTagValue value = DashboardTagValue.FromBulkReadResult(result);

        Assert.False(value.Ok);
        Assert.False(value.QualityGood);
        Assert.Equal("invalid handle", value.Error);
    }

    [Fact]
    public void ActiveAlarm_FromSnapshot_ParsesProviderAndAcknowledgementState()
    {
        ActiveAlarmSnapshot unacked = new()
        {
            AlarmFullReference = "Galaxy!TestArea.TestMachine_001.TestAlarm001",
            Category = "TestArea",
            CurrentState = AlarmConditionState.Active,
            Severity = 500,
        };
        ActiveAlarmSnapshot acked = new()
        {
            AlarmFullReference = "Galaxy!TestArea.TestMachine_002.TestAlarm001",
            CurrentState = AlarmConditionState.ActiveAcked,
        };

        DashboardActiveAlarm unackedRow = DashboardActiveAlarm.FromSnapshot(unacked);
        DashboardActiveAlarm ackedRow = DashboardActiveAlarm.FromSnapshot(acked);

        Assert.Equal("Galaxy", unackedRow.Provider);
        Assert.Equal("TestArea", unackedRow.Area);
        Assert.Equal(500, unackedRow.Severity);
        Assert.True(unackedRow.IsUnacknowledged);
        Assert.False(ackedRow.IsUnacknowledged);
    }

    [Fact]
    public void FormatValue_AndDataType_RenderArrayElementsAndElementType()
    {
        MxArray array = new() { ElementDataType = MxDataType.Double };
        array.Dimensions.Add(3u);
        array.DoubleValues = new DoubleArray();
        array.DoubleValues.Values.Add(new[] { 1.5, 2.25, 3.0 });
        MxValue value = new() { ArrayValue = array };

        Assert.Equal("[1.5, 2.25, 3]", DashboardMxValueFormatter.FormatValue(value));
        Assert.Equal("Double[3]", DashboardMxValueFormatter.FormatDataType(value));
    }
}
