using Akka.Actor;
using Akka.TestKit.Xunit2;
using NSubstitute;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.DataConnection;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Actors;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;

namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests;

/// <summary>Task-10: native alarm subscribe + source-ref routing + unavailable signal.</summary>
public class DataConnectionActorAlarmTests : TestKit
{
    private readonly ISiteHealthCollector _health = Substitute.For<ISiteHealthCollector>();
    private readonly IDataConnectionFactory _factory = Substitute.For<IDataConnectionFactory>();
    private readonly DataConnectionOptions _options = new()
    {
        ReconnectInterval = TimeSpan.FromMilliseconds(100),
        TagResolutionRetryInterval = TimeSpan.FromMilliseconds(200),
        WriteTimeout = TimeSpan.FromSeconds(5)
    };

    private static NativeAlarmTransition Raise(string sourceRef, string sourceObj) =>
        new(sourceRef, sourceObj, "AnalogLimit.Hi", AlarmTransitionKind.Raise,
            new AlarmConditionState(true, false, null, AlarmShelveState.Unshelved, false, 500),
            "Process", "hi", "hi", "", "", null, DateTimeOffset.UtcNow, "92", "90");

    [Fact]
    public void SubscribeAlarms_RoutesTransitionToInstanceSubscriber()
    {
        AlarmTransitionCallback? cb = null;
        var adapter = Substitute.For<IDataConnection, IAlarmSubscribableConnection>();
        adapter.ConnectAsync(Arg.Any<IDictionary<string, string>>(), Arg.Any<CancellationToken>())
            .Returns(Task.CompletedTask);
        ((IAlarmSubscribableConnection)adapter)
            .SubscribeAlarmsAsync(Arg.Any<string>(), Arg.Any<string?>(),
                Arg.Do<AlarmTransitionCallback>(c => cb = c), Arg.Any<CancellationToken>())
            .Returns(Task.FromResult("alarm-sub-1"));

        var actor = Sys.ActorOf(Props.Create(() => new DataConnectionActor(
            "conn", adapter, _options, _health, _factory, "OpcUa")));

        actor.Tell(new SubscribeAlarmsRequest("c", "inst", "conn", "Tank01", null, DateTimeOffset.UtcNow));
        ExpectMsg<SubscribeAlarmsResponse>(m => m.Success);

        Assert.NotNull(cb);
        cb!(Raise("Tank01.Hi", "Tank01"));
        ExpectMsg<NativeAlarmTransitionUpdate>(u => u.Transition.SourceObjectReference == "Tank01");
    }

    [Fact]
    public void SubscribeAlarms_OnNonAlarmCapableAdapter_RepliesFailure()
    {
        var adapter = Substitute.For<IDataConnection>(); // not IAlarmSubscribableConnection
        adapter.ConnectAsync(Arg.Any<IDictionary<string, string>>(), Arg.Any<CancellationToken>())
            .Returns(Task.CompletedTask);

        var actor = Sys.ActorOf(Props.Create(() => new DataConnectionActor(
            "conn", adapter, _options, _health, _factory, "OpcUa")));

        actor.Tell(new SubscribeAlarmsRequest("c", "inst", "conn", "Tank01", null, DateTimeOffset.UtcNow));
        ExpectMsg<SubscribeAlarmsResponse>(m => !m.Success && m.ErrorMessage != null);
    }
}
