using Opc.Ua;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Client.CLI.Commands;
using ZB.MOM.WW.OtOpcUa.Client.CLI.Tests.Fakes;
using ZB.MOM.WW.OtOpcUa.Client.Shared.Models;

namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests;

/// <summary>
/// Regression tests for Client.CLI-009: long-running commands must detach their event handlers
/// from the service before the command finishes, so notifications fired during teardown don't
/// land in a disposed console.
/// </summary>
public class EventHandlerLifecycleTests
{
    /// <summary>Verifies that SubscribeCommand detaches the DataChanged event handler after exit.</summary>
    [Fact]
    public async Task SubscribeCommand_AfterExit_DataChangedEventHasNoSubscribers()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new SubscribeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=Node",
            DurationSeconds = 1
        };

        using var console = TestConsoleHelper.CreateConsole();
        await command.ExecuteAsync(console);

        // The fake's event should have no subscribers after the command completes — every
        // handler attached by the command must have been detached during teardown.
        fakeService.HasDataChangedSubscribers.ShouldBeFalse(
            "SubscribeCommand must detach its DataChanged handler before returning.");
    }

    /// <summary>Verifies that AlarmsCommand detaches the AlarmEvent handler after exit.</summary>
    [Fact]
    public async Task AlarmsCommand_AfterExit_AlarmEventHasNoSubscribers()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new AlarmsCommand(factory)
        {
            Url = "opc.tcp://localhost:4840"
        };

        using var console = TestConsoleHelper.CreateConsole();

        var task = Task.Run(async () => await command.ExecuteAsync(console));

        await fakeService.SubscribeAlarmsInvoked.WaitAsync(TimeSpan.FromSeconds(10));
        console.RequestCancellation();
        await task;

        fakeService.HasAlarmEventSubscribers.ShouldBeFalse(
            "AlarmsCommand must detach its AlarmEvent handler before returning.");
    }
}
