using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;

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

/// <summary>
///     Pins that the GalaxyDriver populates the extended AlarmEventArgs fields
///     (OperatorComment, OriginalRaiseTimestampUtc, AlarmCategory) when the gateway
///     alarm feed delivers a transition with the rich payload, and leaves them null on
///     transitions that don't carry them.
/// </summary>
public sealed class GalaxyDriverAlarmEventArgsExtensionTests
{
    /// <summary>
    /// Verifies that acknowledge transition with full payload populates extended fields.
    /// </summary>
    [Fact]
    public async Task Acknowledge_transition_with_full_payload_populates_extended_fields()
    {
        var feed = new FakeAlarmFeed();
        using var driver = NewDriver(feed);

        await driver.SubscribeAlarmsAsync(["Tank01"], CancellationToken.None);
        var observed = new List<AlarmEventArgs>();
        driver.OnAlarmEvent += (_, args) => observed.Add(args);

        var raise = new DateTime(2026, 5, 1, 12, 0, 0, DateTimeKind.Utc);
        var ack = raise.AddSeconds(45);
        feed.Emit(new GalaxyAlarmTransition(
            AlarmFullReference: "Tank01.Level.HiHi",
            SourceObjectReference: "Tank01",
            AlarmTypeName: "AnalogLimitAlarm.HiHi",
            TransitionKind: GalaxyAlarmTransitionKind.Acknowledge,
            SeverityBucket: AlarmSeverity.Critical,
            OpcUaSeverity: 800,
            RawMxAccessSeverity: 750,
            OriginalRaiseTimestampUtc: raise,
            TransitionTimestampUtc: ack,
            OperatorUser: "alice",
            OperatorComment: "investigating",
            Category: "Process",
            Description: "Tank 01 high-high level"));

        observed.ShouldHaveSingleItem();
        observed[0].OperatorComment.ShouldBe("investigating");
        observed[0].OriginalRaiseTimestampUtc.ShouldBe(raise);
        observed[0].AlarmCategory.ShouldBe("Process");
    }

    /// <summary>
    /// Verifies that raise transition without optional fields leaves them null.
    /// </summary>
    [Fact]
    public async Task Raise_transition_without_optional_fields_leaves_them_null()
    {
        var feed = new FakeAlarmFeed();
        using var driver = NewDriver(feed);

        await driver.SubscribeAlarmsAsync(["Tank01"], CancellationToken.None);
        var observed = new List<AlarmEventArgs>();
        driver.OnAlarmEvent += (_, args) => observed.Add(args);

        feed.Emit(new GalaxyAlarmTransition(
            AlarmFullReference: "Tank01.Level.HiHi",
            SourceObjectReference: string.Empty,
            AlarmTypeName: "AnalogLimitAlarm.HiHi",
            TransitionKind: GalaxyAlarmTransitionKind.Raise,
            SeverityBucket: AlarmSeverity.Critical,
            OpcUaSeverity: 800,
            RawMxAccessSeverity: 750,
            OriginalRaiseTimestampUtc: null,
            TransitionTimestampUtc: DateTime.UtcNow,
            OperatorUser: string.Empty,
            OperatorComment: string.Empty,
            Category: string.Empty,
            Description: string.Empty));

        observed.ShouldHaveSingleItem();
        observed[0].OperatorComment.ShouldBeNull();
        observed[0].OriginalRaiseTimestampUtc.ShouldBeNull();
        observed[0].AlarmCategory.ShouldBeNull();
    }

    private static GalaxyDriver NewDriver(IGalaxyAlarmFeed feed)
    {
        var options = new GalaxyDriverOptions(
            new GalaxyGatewayOptions("http://localhost:5000", "literal-api-key"),
            new GalaxyMxAccessOptions("AlarmExtensionTest"),
            new GalaxyRepositoryOptions(),
            new GalaxyReconnectOptions());
        return new GalaxyDriver(
            driverInstanceId: "drv-1",
            options: options,
            hierarchySource: null,
            alarmFeed: feed);
    }

    /// <summary>In-memory <see cref="IGalaxyAlarmFeed"/> the test drives directly.</summary>
    private sealed class FakeAlarmFeed : IGalaxyAlarmFeed
    {
        /// <summary>
        /// Occurs when an alarm transition is raised.
        /// </summary>
        public event EventHandler<GalaxyAlarmTransition>? OnAlarmTransition;

        /// <summary>
        /// Starts the alarm feed.
        /// </summary>
        public void Start() { }

        /// <summary>
        /// Emits an alarm transition to subscribers.
        /// </summary>
        /// <param name="transition">The alarm transition to emit.</param>
        public void Emit(GalaxyAlarmTransition transition)
            => OnAlarmTransition?.Invoke(this, transition);

        /// <summary>
        /// Disposes the alarm feed asynchronously.
        /// </summary>
        public ValueTask DisposeAsync() => ValueTask.CompletedTask;
    }
}
