using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Proxy;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Proxy.Tests;

/// <summary>
///     Pins <see cref="GalaxyProxyDriver.ToHistoricalEvent"/> — the wire-to-domain mapping
///     from <see cref="GalaxyHistoricalEvent"/> (MessagePack-annotated IPC contract,
///     Unix-ms timestamps) to <c>Core.Abstractions.HistoricalEvent</c> (domain record,
///     <see cref="DateTime"/> timestamps). Added in PR 35 alongside the new
///     <c>IHistoryProvider.ReadEventsAsync</c> method.
/// </summary>
[Trait("Category", "Unit")]
public sealed class HistoricalEventMappingTests
{
    [Fact]
    public void Maps_every_field_from_wire_to_domain_record()
    {
        var wire = new GalaxyHistoricalEvent
        {
            EventId = "evt-42",
            SourceName = "Tank1.HiAlarm",
            EventTimeUtcUnixMs = 1_700_000_000_000L, // 2023-11-14T22:13:20.000Z
            ReceivedTimeUtcUnixMs = 1_700_000_000_500L,
            DisplayText = "High level reached",
            Severity = 750,
        };

        var domain = GalaxyProxyDriver.ToHistoricalEvent(wire);

        domain.EventId.ShouldBe("evt-42");
        domain.SourceName.ShouldBe("Tank1.HiAlarm");
        domain.EventTimeUtc.ShouldBe(new DateTime(2023, 11, 14, 22, 13, 20, DateTimeKind.Utc));
        domain.ReceivedTimeUtc.ShouldBe(new DateTime(2023, 11, 14, 22, 13, 20, 500, DateTimeKind.Utc));
        domain.Message.ShouldBe("High level reached");
        domain.Severity.ShouldBe((ushort)750);
    }

    [Fact]
    public void Preserves_null_SourceName_and_DisplayText()
    {
        // Historical rows from the Galaxy event historian often omit source or message for
        // system events (e.g. time sync). The mapping must preserve null — callers use it to
        // distinguish system events from alarm events.
        var wire = new GalaxyHistoricalEvent
        {
            EventId = "sys-1",
            SourceName = null,
            EventTimeUtcUnixMs = 0,
            ReceivedTimeUtcUnixMs = 0,
            DisplayText = null,
            Severity = 1,
        };

        var domain = GalaxyProxyDriver.ToHistoricalEvent(wire);

        domain.SourceName.ShouldBeNull();
        domain.Message.ShouldBeNull();
    }

    [Fact]
    public void EventTime_and_ReceivedTime_are_produced_as_DateTimeKind_Utc()
    {
        // Unix-ms timestamps come off the wire timezone-agnostic; the mapping must tag the
        // resulting DateTime as Utc so downstream serializers (JSON, OPC UA types) don't apply
        // an unexpected local-time offset.
        var wire = new GalaxyHistoricalEvent
        {
            EventId = "e",
            EventTimeUtcUnixMs = 1_000L,
            ReceivedTimeUtcUnixMs = 2_000L,
        };

        var domain = GalaxyProxyDriver.ToHistoricalEvent(wire);

        domain.EventTimeUtc.Kind.ShouldBe(DateTimeKind.Utc);
        domain.ReceivedTimeUtc.Kind.ShouldBe(DateTimeKind.Utc);
    }
}
