using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

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

/// <summary>
/// Task 28: live smoke test for the OPC UA Alarms &amp; Conditions adapter
/// (<see cref="OpcUaDataConnection"/> as <c>IAlarmSubscribableConnection</c>).
///
/// Round-trips against an alarm-capable OPC UA endpoint
/// (<c>opc.tcp://localhost:50000</c> — the infra OPC PLC server, see
/// <c>infra/docker-compose.yml</c>). Marked <c>[SkippableFact]</c> so it reports
/// Skipped — not failed — when the endpoint is unreachable OR does not expose
/// Alarms &amp; Conditions (no ConditionRefresh snapshot arrives). The OPC PLC
/// simulator does not reliably expose A&amp;C; see
/// <c>docs/test_infra/test_infra.md</c> for the alarm-capable-server requirement.
///
/// The pure field→transition mapping is covered without a server by
/// <see cref="Adapters.OpcUaAlarmMapperTests"/>; this test proves the live
/// event-subscription + ConditionRefresh path end to end when infra supports it.
/// </summary>
[Trait("Category", "RequiresOpcUa")]
public class OpcUaAlarmLiveSmokeTests
{
    private const string EndpointUrl = "opc.tcp://localhost:50000";

    [SkippableFact]
    public async Task SubscribeAlarms_DeliversConditionRefreshSnapshot()
    {
        using var loggerFactory = LoggerFactory.Create(_ => { });
        var clientFactory = new RealOpcUaClientFactory(new OpcUaGlobalOptions(), loggerFactory);
        var adapter = new OpcUaDataConnection(clientFactory, NullLogger<OpcUaDataConnection>.Instance);

        // Probe the endpoint. An unreachable infra server surfaces a socket/timeout
        // error from deep in the OPC Foundation SDK — treat as "infra not available".
        try
        {
            await adapter.ConnectAsync(new Dictionary<string, string> { ["EndpointUrl"] = EndpointUrl });
        }
        catch (Exception ex)
        {
            Skip.If(true, $"OPC UA test server not reachable on {EndpointUrl}: {ex.Message}");
            return;
        }

        try
        {
            var snapshotComplete = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            void OnTransition(NativeAlarmTransition t)
            {
                if (t.Kind == AlarmTransitionKind.SnapshotComplete)
                {
                    snapshotComplete.TrySetResult(true);
                }
            }

            // Empty source reference = mirror every condition under the server's
            // event notifier; ConditionRefresh replays the active set then a
            // SnapshotComplete sentinel.
            await adapter.SubscribeAlarmsAsync(sourceReference: string.Empty, conditionFilter: null, OnTransition);

            var done = await Task.WhenAny(snapshotComplete.Task, Task.Delay(TimeSpan.FromSeconds(10)));

            // Reachable but no A&C snapshot within the window → the server does not
            // expose Alarms & Conditions. Skip rather than fail.
            Skip.IfNot(done == snapshotComplete.Task,
                $"OPC UA endpoint {EndpointUrl} reachable but delivered no A&C ConditionRefresh snapshot — " +
                "server likely does not expose Alarms & Conditions.");

            Assert.True(snapshotComplete.Task.IsCompletedSuccessfully);
        }
        finally
        {
            await adapter.DisconnectAsync();
        }
    }
}
