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;

public class HistoryReadCommandTests
{
    /// <summary>Verifies RawRead execution prints values.</summary>
    [Fact]
    public async Task Execute_RawRead_PrintsValues()
    {
        var time1 = new DateTime(2025, 6, 15, 10, 0, 0, DateTimeKind.Utc);
        var time2 = new DateTime(2025, 6, 15, 11, 0, 0, DateTimeKind.Utc);
        var fakeService = new FakeOpcUaClientService
        {
            HistoryReadResult = new List<DataValue>
            {
                new(new Variant(10.5), StatusCodes.Good, time1, time1),
                new(new Variant(20.3), StatusCodes.Good, time2, time2)
            }
        };
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode",
            StartTime = "2025-06-15T00:00:00Z",
            EndTime = "2025-06-15T23:59:59Z"
        };

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

        var output = TestConsoleHelper.GetOutput(console);
        output.ShouldContain("History for ns=2;s=HistNode");
        output.ShouldContain("Timestamp");
        output.ShouldContain("Value");
        output.ShouldContain("Status");
        output.ShouldContain("2 values returned.");
    }

    /// <summary>Verifies RawRead execution calls HistoryReadRaw.</summary>
    [Fact]
    public async Task Execute_RawRead_CallsHistoryReadRaw()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode",
            MaxValues = 500
        };

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

        fakeService.HistoryReadRawCalls.Count.ShouldBe(1);
        fakeService.HistoryReadRawCalls[0].MaxValues.ShouldBe(500);
        fakeService.HistoryReadRawCalls[0].NodeId.Identifier.ShouldBe("HistNode");
    }

    /// <summary>Verifies AggregateRead execution calls HistoryReadAggregate.</summary>
    [Fact]
    public async Task Execute_AggregateRead_CallsHistoryReadAggregate()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode",
            Aggregate = "Average",
            IntervalMs = 60000
        };

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

        fakeService.HistoryReadAggregateCalls.Count.ShouldBe(1);
        fakeService.HistoryReadAggregateCalls[0].Aggregate.ShouldBe(AggregateType.Average);
        fakeService.HistoryReadAggregateCalls[0].IntervalMs.ShouldBe(60000);
    }

    /// <summary>Verifies AggregateRead execution prints aggregate info.</summary>
    [Fact]
    public async Task Execute_AggregateRead_PrintsAggregateInfo()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode",
            Aggregate = "Maximum",
            IntervalMs = 7200000
        };

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

        var output = TestConsoleHelper.GetOutput(console);
        output.ShouldContain("Maximum");
        output.ShouldContain("7200000");
    }

    /// <summary>Verifies invalid aggregate throws CommandException.</summary>
    [Fact]
    public async Task Execute_InvalidAggregate_ThrowsCommandException()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode",
            Aggregate = "InvalidAgg"
        };

        using var console = TestConsoleHelper.CreateConsole();
        // Operator-input errors now surface as CliFx CommandException (was ArgumentException)
        // per Client.CLI-006 so malformed input prints a clean CLI error instead of a stack trace.
        await Should.ThrowAsync<CliFx.Exceptions.CommandException>(
            async () => await command.ExecuteAsync(console));
    }

    /// <summary>Verifies disconnect is called in finally block.</summary>
    [Fact]
    public async Task Execute_DisconnectsInFinally()
    {
        var fakeService = new FakeOpcUaClientService();
        var factory = new FakeOpcUaClientServiceFactory(fakeService);
        var command = new HistoryReadCommand(factory)
        {
            Url = "opc.tcp://localhost:4840",
            NodeId = "ns=2;s=HistNode"
        };

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

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