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

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

public class AcknowledgeCommandTests
{
    private static string HexOf(byte[] bytes) => Convert.ToHexString(bytes);

    /// <summary>Verifies that a successful ack prints a success message.</summary>
    [Fact]
    public async Task Execute_Success_PrintsSuccessMessage()
    {
        var fakeService = new FakeOpcUaClientService
        {
            AcknowledgeAlarmResult = StatusCodes.Good
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var eventId = new byte[] { 0x01, 0x02, 0x03 };
        var command = new AcknowledgeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=MyAlarm",
            EventId = HexOf(eventId),
            Comment = "test comment"
        };

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

        var output = TestConsoleHelper.GetOutput(console);
        output.ShouldContain("Acknowledge successful");
        fakeService.AcknowledgeAlarmCalls.Count.ShouldBe(1);
        fakeService.AcknowledgeAlarmCalls[0].ConditionNodeId.ShouldBe("ns=2;s=MyAlarm");
        fakeService.AcknowledgeAlarmCalls[0].EventId.ShouldBe(eventId);
        fakeService.AcknowledgeAlarmCalls[0].Comment.ShouldBe("test comment");
    }

    /// <summary>Verifies that a bad StatusCode from the service prints a failure message.</summary>
    [Fact]
    public async Task Execute_BadStatus_PrintsFailureMessage()
    {
        var fakeService = new FakeOpcUaClientService
        {
            AcknowledgeAlarmResult = StatusCodes.BadEventNotAcknowledgeable
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new AcknowledgeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=MyAlarm",
            EventId = HexOf(new byte[] { 0xAB }),
            Comment = ""
        };

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

        var output = TestConsoleHelper.GetOutput(console);
        output.ShouldContain("Acknowledge failed");
    }

    /// <summary>Verifies that an invalid hex event-id throws CommandException.</summary>
    [Fact]
    public async Task Execute_InvalidEventId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new AcknowledgeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=MyAlarm",
            EventId = "not-hex!",
            Comment = ""
        };

        using var console = TestConsoleHelper.CreateConsole();
        var ex = await Should.ThrowAsync<CommandException>(async () => await command.ExecuteAsync(console));
        ex.Message.ShouldContain("event-id", Case.Insensitive);
    }

    /// <summary>Verifies that the command disconnects in the finally block.</summary>
    [Fact]
    public async Task Execute_DisconnectsInFinally()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new AcknowledgeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=MyAlarm",
            EventId = HexOf(new byte[] { 0x01 }),
            Comment = ""
        };

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

        fakeService.DisconnectCalled.ShouldBeTrue();
        fakeService.DisposeCalled.ShouldBeTrue();
    }
}
