using CliFx.Exceptions;
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;

/// <summary>
/// Regression tests for Client.CLI-011: alarm-op commands (ack, confirm, enable, disable, shelve)
/// must validate the --node option with a clean CommandException rather than letting a raw
/// exception propagate from NodeId.Parse inside the service.
/// </summary>
public class AlarmOpCommandNodeIdValidationTests
{
    private static string HexOf(byte[] bytes) => Convert.ToHexString(bytes);

    /// <summary>AcknowledgeCommand with an invalid node ID must throw CommandException, not a raw exception.</summary>
    [Fact]
    public async Task AcknowledgeCommand_InvalidNodeId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new AcknowledgeCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "not-a-node-id",
            EventId = HexOf(new byte[] { 0x01 }),
            Comment = ""
        };

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

    /// <summary>ConfirmCommand with an invalid node ID must throw CommandException, not a raw exception.</summary>
    [Fact]
    public async Task ConfirmCommand_InvalidNodeId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new ConfirmCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "not-a-node-id",
            EventId = HexOf(new byte[] { 0x01 }),
            Comment = ""
        };

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

    /// <summary>EnableCommand with an invalid node ID must throw CommandException, not a raw exception.</summary>
    [Fact]
    public async Task EnableCommand_InvalidNodeId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new EnableCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "not-a-node-id"
        };

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

    /// <summary>DisableCommand with an invalid node ID must throw CommandException, not a raw exception.</summary>
    [Fact]
    public async Task DisableCommand_InvalidNodeId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new DisableCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "not-a-node-id"
        };

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

    /// <summary>ShelveCommand with an invalid node ID must throw CommandException, not a raw exception.</summary>
    [Fact]
    public async Task ShelveCommand_InvalidNodeId_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new ShelveCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "not-a-node-id",
            Kind = "OneShot",
            DurationSeconds = 0
        };

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