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 WriteCommandTests
{
    /// <summary>Verifies that write command executes successfully.</summary>
    [Fact]
    public async Task Execute_WritesSuccessfully()
    {
        var fakeService = new FakeOpcUaClientService
        {
            ReadValueResult = new DataValue(new Variant(42)),
            WriteStatusCodeResult = StatusCodes.Good
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new WriteCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=MyVar",
            Value = "100"
        };

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

        var output = TestConsoleHelper.GetOutput(console);
        output.ShouldContain("Write successful: ns=2;s=MyVar = 100");
    }

    /// <summary>Verifies that write command reports failure.</summary>
    [Fact]
    public async Task Execute_ReportsFailure()
    {
        var fakeService = new FakeOpcUaClientService
        {
            ReadValueResult = new DataValue(new Variant("current")),
            WriteStatusCodeResult = StatusCodes.BadNotWritable
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new WriteCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=ReadOnly",
            Value = "newvalue"
        };

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

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

    /// <summary>Verifies that write command reads current value before writing.</summary>
    [Fact]
    public async Task Execute_ReadsCurrentValueThenWrites()
    {
        var fakeService = new FakeOpcUaClientService
        {
            ReadValueResult = new DataValue(new Variant(3.14)),
            WriteStatusCodeResult = StatusCodes.Good
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new WriteCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=FloatVar",
            Value = "2.718"
        };

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

        // Should read first to get current type, then write
        fakeService.ReadNodeIds.Count.ShouldBe(1);
        fakeService.WriteValues.Count.ShouldBe(1);
        fakeService.WriteValues[0].Value.ShouldBeOfType<double>();
    }

    /// <summary>Verifies that write command disconnects in finally block.</summary>
    [Fact]
    public async Task Execute_DisconnectsInFinally()
    {
        var fakeService = new FakeOpcUaClientService
        {
            ReadValueResult = new DataValue(new Variant("test"))
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new WriteCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=Node",
            Value = "value"
        };

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

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