using MessagePack;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Ipc;

namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests;

/// <summary>
///     End-to-end tests for <see cref="WonderwareHistorianClient"/>: every interface method
///     round-trips through a real named pipe against the in-process
///     <see cref="FakeSidecarServer"/>, which reuses the client's own byte-identical framing
///     code. Covers byte→uint quality mapping, BadNoData propagation for null aggregate
///     buckets, alarm-write per-event status flow, Hello handshake rejection on bad secret,
///     and reconnect after a transport drop.
/// </summary>
public sealed class WonderwareHistorianClientTests
{
    private const string Secret = "test-secret-123";

    private static string UniquePipeName() => $"otopcua-historian-test-{Guid.NewGuid():N}";

    private static WonderwareHistorianClientOptions OptsFor(string pipe) => new(
        PipeName: pipe,
        SharedSecret: Secret,
        PeerName: "test",
        ConnectTimeout: TimeSpan.FromSeconds(2),
        CallTimeout: TimeSpan.FromSeconds(2));

    /// <summary>Verifies that ReadRawAsync round-trips samples and maps quality bytes to OPC UA status codes.</summary>
    [Fact]
    public async Task ReadRawAsync_RoundTripsSamples_AndMapsQualityByteToOpcUaStatusCode()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadRaw = req => new ReadRawReply
            {
                Success = true,
                Samples =
                [
                    new HistorianSampleDto
                    {
                        ValueBytes = MessagePackSerializer.Serialize<object>(42.0),
                        Quality = 192, // Good
                        TimestampUtcTicks = new DateTime(2026, 4, 29, 12, 0, 0, DateTimeKind.Utc).Ticks,
                    },
                    new HistorianSampleDto
                    {
                        ValueBytes = MessagePackSerializer.Serialize<object>(43.5),
                        Quality = 8, // Bad_NotConnected
                        TimestampUtcTicks = new DateTime(2026, 4, 29, 12, 0, 1, DateTimeKind.Utc).Ticks,
                    },
                ],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var result = await client.ReadRawAsync("Tank.Level",
            new DateTime(2026, 4, 29, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2026, 4, 30, 0, 0, 0, DateTimeKind.Utc),
            100, CancellationToken.None);

        result.ContinuationPoint.ShouldBeNull();
        result.Samples.Count.ShouldBe(2);
        result.Samples[0].StatusCode.ShouldBe(0x00000000u); // Good
        result.Samples[0].SourceTimestampUtc.ShouldBe(new DateTime(2026, 4, 29, 12, 0, 0, DateTimeKind.Utc));
        result.Samples[1].StatusCode.ShouldBe(0x808A0000u); // Bad_NotConnected
    }

    /// <summary>Verifies that ReadProcessedAsync maps null buckets to BadNoData status.</summary>
    [Fact]
    public async Task ReadProcessedAsync_NullBuckets_MapToBadNoData()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadProcessed = _ => new ReadProcessedReply
            {
                Success = true,
                Buckets =
                [
                    new HistorianAggregateSampleDto { Value = 50.0, TimestampUtcTicks = new DateTime(2026, 4, 29, 0, 0, 0, DateTimeKind.Utc).Ticks },
                    new HistorianAggregateSampleDto { Value = null, TimestampUtcTicks = new DateTime(2026, 4, 29, 0, 1, 0, DateTimeKind.Utc).Ticks },
                ],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var result = await client.ReadProcessedAsync("Tank.Level",
            new DateTime(2026, 4, 29, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2026, 4, 29, 0, 2, 0, DateTimeKind.Utc),
            TimeSpan.FromMinutes(1), HistoryAggregateType.Average, CancellationToken.None);

        result.Samples.Count.ShouldBe(2);
        result.Samples[0].StatusCode.ShouldBe(0x00000000u); // Good
        result.Samples[0].Value.ShouldBe(50.0);
        result.Samples[1].StatusCode.ShouldBe(0x800E0000u); // BadNoData
        result.Samples[1].Value.ShouldBeNull();
    }

    /// <summary>Verifies that ReadAtTimeAsync preserves timestamp order.</summary>
    [Fact]
    public async Task ReadAtTimeAsync_PreservesTimestampOrder()
    {
        var pipe = UniquePipeName();
        var t1 = new DateTime(2026, 4, 29, 1, 0, 0, DateTimeKind.Utc);
        var t2 = new DateTime(2026, 4, 29, 2, 0, 0, DateTimeKind.Utc);

        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadAtTime = req => new ReadAtTimeReply
            {
                Success = true,
                Samples = req.TimestampsUtcTicks
                    .Select(ticks => new HistorianSampleDto { Quality = 192, TimestampUtcTicks = ticks })
                    .ToArray(),
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var result = await client.ReadAtTimeAsync("Tank.Level", new[] { t1, t2 }, CancellationToken.None);

        result.Samples.Count.ShouldBe(2);
        result.Samples[0].SourceTimestampUtc.ShouldBe(t1);
        result.Samples[1].SourceTimestampUtc.ShouldBe(t2);
    }

    /// <summary>Verifies that ReadAtTimeAsync aligns by timestamp and fills gaps with bad status.</summary>
    [Fact]
    public async Task ReadAtTimeAsync_PartialAndReorderedReply_AlignsByTimestamp_AndFillsGapsAsBad()
    {
        var pipe = UniquePipeName();
        var t1 = new DateTime(2026, 4, 29, 1, 0, 0, DateTimeKind.Utc);
        var t2 = new DateTime(2026, 4, 29, 2, 0, 0, DateTimeKind.Utc);
        var t3 = new DateTime(2026, 4, 29, 3, 0, 0, DateTimeKind.Utc);

        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            // Sidecar returns only t3 and t1 (out of order), drops t2 entirely. A
            // contract-compliant client must realign by timestamp and synthesize a
            // Bad-quality snapshot for the missing t2.
            OnReadAtTime = _ => new ReadAtTimeReply
            {
                Success = true,
                Samples =
                [
                    new HistorianSampleDto
                    {
                        ValueBytes = MessagePackSerializer.Serialize<object>(3.0),
                        Quality = 192, TimestampUtcTicks = t3.Ticks,
                    },
                    new HistorianSampleDto
                    {
                        ValueBytes = MessagePackSerializer.Serialize<object>(1.0),
                        Quality = 192, TimestampUtcTicks = t1.Ticks,
                    },
                ],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var result = await client.ReadAtTimeAsync("Tank.Level", new[] { t1, t2, t3 }, CancellationToken.None);

        // Result MUST be the same length and order as the request.
        result.Samples.Count.ShouldBe(3);

        result.Samples[0].SourceTimestampUtc.ShouldBe(t1);
        result.Samples[0].StatusCode.ShouldBe(0x00000000u); // Good
        result.Samples[0].Value.ShouldBe(1.0);

        // t2 was not returned by the sidecar → Bad-quality gap snapshot at the requested time.
        result.Samples[1].SourceTimestampUtc.ShouldBe(t2);
        result.Samples[1].StatusCode.ShouldBe(0x80000000u); // Bad
        result.Samples[1].Value.ShouldBeNull();

        result.Samples[2].SourceTimestampUtc.ShouldBe(t3);
        result.Samples[2].StatusCode.ShouldBe(0x00000000u); // Good
        result.Samples[2].Value.ShouldBe(3.0);
    }

    /// <summary>Verifies that ReadEventsAsync preserves event field values.</summary>
    [Fact]
    public async Task ReadEventsAsync_PreservesEventFields()
    {
        var pipe = UniquePipeName();
        var eid = Guid.NewGuid().ToString("N");
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadEvents = _ => new ReadEventsReply
            {
                Success = true,
                Events =
                [
                    new HistorianEventDto
                    {
                        EventId = eid, Source = "Tank.HiHi",
                        EventTimeUtcTicks = new DateTime(2026, 4, 29, 1, 0, 0, DateTimeKind.Utc).Ticks,
                        ReceivedTimeUtcTicks = new DateTime(2026, 4, 29, 1, 0, 1, DateTimeKind.Utc).Ticks,
                        DisplayText = "Level high-high", Severity = 800,
                    },
                ],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var result = await client.ReadEventsAsync("Tank.HiHi",
            new DateTime(2026, 4, 29, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2026, 4, 30, 0, 0, 0, DateTimeKind.Utc),
            100, CancellationToken.None);

        result.Events.Count.ShouldBe(1);
        result.Events[0].EventId.ShouldBe(eid);
        result.Events[0].SourceName.ShouldBe("Tank.HiHi");
        result.Events[0].Message.ShouldBe("Level high-high");
        result.Events[0].Severity.ShouldBe((ushort)800);
    }

    /// <summary>Verifies that ReadRawAsync throws InvalidOperationException on server errors.</summary>
    [Fact]
    public async Task ReadRawAsync_ServerError_ThrowsInvalidOperation()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadRaw = _ => new ReadRawReply { Success = false, Error = "historian unreachable" },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        var ex = await Should.ThrowAsync<InvalidOperationException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 100, CancellationToken.None));
        ex.Message.ShouldContain("historian unreachable");
    }

    /// <summary>Verifies that WriteBatchAsync maps per-event results to acknowledge or retry outcomes.</summary>
    [Fact]
    public async Task WriteBatchAsync_PerEventOk_MapsToAckOrRetryPlease()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnWriteAlarmEvents = req => new WriteAlarmEventsReply
            {
                Success = true,
                PerEventOk = req.Events.Select(e => e.EventId != "ev-fail").ToArray(),
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var batch = new[]
        {
            new AlarmHistorianEvent("ev-1", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Activated", "msg", "operator", null, DateTime.UtcNow),
            new AlarmHistorianEvent("ev-fail", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Acknowledged", "msg", "operator", null, DateTime.UtcNow),
        };

        var outcomes = await client.WriteBatchAsync(batch, CancellationToken.None);

        outcomes.Count.ShouldBe(2);
        outcomes[0].ShouldBe(HistorianWriteOutcome.Ack);
        outcomes[1].ShouldBe(HistorianWriteOutcome.RetryPlease);
    }

    /// <summary>Verifies that WriteBatchAsync returns retry outcomes for whole call failures.</summary>
    [Fact]
    public async Task WriteBatchAsync_WholeCallFailure_ReturnsRetryPleaseForEveryEvent()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnWriteAlarmEvents = _ => new WriteAlarmEventsReply
            {
                Success = false,
                Error = "historian event-store down",
                PerEventOk = new bool[2],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var batch = new[]
        {
            new AlarmHistorianEvent("ev-1", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Activated", "msg", "u", null, DateTime.UtcNow),
            new AlarmHistorianEvent("ev-2", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Cleared",   "msg", "u", null, DateTime.UtcNow),
        };

        var outcomes = await client.WriteBatchAsync(batch, CancellationToken.None);

        outcomes.Count.ShouldBe(2);
        outcomes[0].ShouldBe(HistorianWriteOutcome.RetryPlease);
        outcomes[1].ShouldBe(HistorianWriteOutcome.RetryPlease);
    }

    /// <summary>Verifies that Hello handshake throws UnauthorizedAccessException on secret mismatch.</summary>
    [Fact]
    public async Task Hello_BadSecret_ThrowsUnauthorizedAccess()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, "different-secret");
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        var ex = await Should.ThrowAsync<UnauthorizedAccessException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 100, CancellationToken.None));
        ex.Message.ShouldContain("shared-secret-mismatch");
    }

    /// <summary>Verifies that the client retries after a transport drop.</summary>
    [Fact]
    public async Task Reconnect_AfterTransportDrop_RetriesOnce()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            // First connection drops after handshake → client retries on next call.
            DisconnectAfterHandshake = true,
            OnReadRaw = req => new ReadRawReply
            {
                Success = true,
                Samples = [new HistorianSampleDto { Quality = 192, TimestampUtcTicks = req.StartUtcTicks }],
            },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        // First call: handshake + dropped. Reconnect kicks in inside the channel; second
        // attempt within the same InvokeAsync succeeds. From the caller's perspective it's
        // one ReadRawAsync that returns a sample.
        var result = await client.ReadRawAsync("Tag",
            new DateTime(2026, 4, 29, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2026, 4, 30, 0, 0, 0, DateTimeKind.Utc),
            100, CancellationToken.None);

        result.Samples.Count.ShouldBe(1);
    }

    /// <summary>Verifies that GetHealthSnapshot tracks success and failure counts.</summary>
    [Fact]
    public async Task GetHealthSnapshot_TracksSuccessAndFailureCounts()
    {
        var pipe = UniquePipeName();
        var failNext = false;
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadRaw = _ => failNext
                ? new ReadRawReply { Success = false, Error = "boom" }
                : new ReadRawReply { Success = true },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        await client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 1, CancellationToken.None);

        failNext = true;
        await Should.ThrowAsync<InvalidOperationException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 1, CancellationToken.None));

        var snap = client.GetHealthSnapshot();
        snap.TotalQueries.ShouldBe(2);
        snap.TotalSuccesses.ShouldBe(1);
        snap.TotalFailures.ShouldBe(1);
        snap.ConsecutiveFailures.ShouldBe(1);
        snap.LastError.ShouldNotBeNull();
        snap.ProcessConnectionOpen.ShouldBeTrue();
    }

    // ===== Finding-009: missing edge-case tests =====

    /// <summary>
    ///     (2) A transport drop during a write (the catch path in WriteBatchAsync) must return
    ///     RetryPlease for every event in the batch — never throw, never PermanentFail.
    /// </summary>
    [Fact]
    public async Task WriteBatchAsync_TransportDropDuringWrite_ReturnsRetryPleaseForEveryEvent()
    {
        var pipe = UniquePipeName();
        // Server disconnects before replying to the write request. The client's single retry
        // reconnects; on the second attempt the server is still armed to disconnect, so both
        // attempts fail and the catch block fires.
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            DisconnectBeforeReply = true,
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));
        var batch = new[]
        {
            new AlarmHistorianEvent("ev-1", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Activated", "msg", "u", null, DateTime.UtcNow),
            new AlarmHistorianEvent("ev-2", "Tank/HiHi", "HiHi", "LimitAlarm", AlarmSeverity.High, "Cleared",   "msg", "u", null, DateTime.UtcNow),
        };

        // WriteBatchAsync must not throw — it absorbs transport failures as RetryPlease.
        var outcomes = await client.WriteBatchAsync(batch, CancellationToken.None);

        outcomes.Count.ShouldBe(2);
        outcomes[0].ShouldBe(HistorianWriteOutcome.RetryPlease);
        outcomes[1].ShouldBe(HistorianWriteOutcome.RetryPlease);
    }

    /// <summary>
    ///     (3) When both the first attempt and the single retry fail (the "second attempt also
    ///     fails" path in InvokeAsync), the exception propagates to the caller.
    /// </summary>
    [Fact]
    public async Task InvokeAsync_BothAttemptsFailTransport_PropagatesException()
    {
        var pipe = UniquePipeName();
        // DisconnectBeforeReply stays true so both the first attempt and the single retry
        // inside InvokeAsync are dropped, causing the second ExchangeAsync to throw.
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            DisconnectBeforeReply = true,
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        // ReadRawAsync uses Invoke, which propagates the exception when both attempts fail.
        await Should.ThrowAsync<Exception>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 100, CancellationToken.None));
    }

    /// <summary>
    ///     (4) A stalled sidecar that never sends a reply must cause an
    ///     <see cref="OperationCanceledException"/> within the configured CallTimeout.
    /// </summary>
    [Fact]
    public async Task ReadRawAsync_StalledSidecar_TimesOutWithOperationCanceledException()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            StallAfterRequest = true,
        };
        await server.StartAsync();

        var opts = new WonderwareHistorianClientOptions(
            PipeName: pipe,
            SharedSecret: Secret,
            PeerName: "test",
            ConnectTimeout: TimeSpan.FromSeconds(2),
            CallTimeout: TimeSpan.FromMilliseconds(500)); // short timeout for test speed

        await using var client = new WonderwareHistorianClient(opts);

        // The stall means neither the first nor the retry can complete, so the timeout
        // linked-token should cancel the operation.
        await Should.ThrowAsync<OperationCanceledException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 100, CancellationToken.None));
    }

    /// <summary>
    ///     (5) <see cref="HistoryAggregateType.Total"/> must throw
    ///     <see cref="NotSupportedException"/> because Wonderware AnalogSummary has no Total
    ///     aggregate column.
    /// </summary>
    [Fact]
    public async Task ReadProcessedAsync_TotalAggregate_ThrowsNotSupported()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret);
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        await Should.ThrowAsync<NotSupportedException>(() =>
            client.ReadProcessedAsync("Tag",
                DateTime.UtcNow, DateTime.UtcNow, TimeSpan.FromMinutes(1),
                HistoryAggregateType.Total, CancellationToken.None));
    }

    /// <summary>
    ///     (6) When the sidecar replies with a <see cref="MessageKind"/> the client does not
    ///     expect (e.g. ReadRawReply where ReadAtTimeReply was expected), the client must throw
    ///     <see cref="InvalidDataException"/>.
    /// </summary>
    [Fact]
    public async Task ReadRawAsync_SidecarRepliesWithWrongKind_ThrowsInvalidDataException()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            // Force the server to reply with ReadAtTimeReply instead of ReadRawReply.
            ReplyWithWrongKind = MessageKind.ReadAtTimeReply,
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        await Should.ThrowAsync<InvalidDataException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 100, CancellationToken.None));
    }

    // ===== Finding-003 / Finding-004: health counter consistency =====

    /// <summary>
    ///     (Finding 003 + 004) A sidecar-level failure must be classified once: TotalSuccesses
    ///     must stay at 0, TotalFailures must become 1, and TotalQueries / TotalSuccesses /
    ///     TotalFailures must all be updated under the same lock so a concurrent snapshot can
    ///     never observe inflated successes or out-of-band TotalQueries. This pins behaviour so
    ///     a future regression to the "RecordSuccess then undo via ReclassifySuccessAsFailure"
    ///     dance is caught.
    /// </summary>
    [Fact]
    public async Task GetHealthSnapshot_SidecarFailure_NeverInflatesSuccessCounter()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadRaw = _ => new ReadRawReply { Success = false, Error = "boom" },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        await Should.ThrowAsync<InvalidOperationException>(() =>
            client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 1, CancellationToken.None));

        var snap = client.GetHealthSnapshot();
        snap.TotalQueries.ShouldBe(1);
        snap.TotalSuccesses.ShouldBe(0);
        snap.TotalFailures.ShouldBe(1);
        snap.ConsecutiveFailures.ShouldBe(1);
        snap.LastError.ShouldNotBeNull();
    }

    /// <summary>
    ///     (Finding 003) Concurrent calls + concurrent <see cref="WonderwareHistorianClient.GetHealthSnapshot"/>
    ///     reads must observe consistent counters. Specifically, TotalSuccesses + TotalFailures
    ///     must equal TotalQueries at every observed snapshot (no torn read between an
    ///     Interlocked-incremented TotalQueries and a lock-protected outcome counter). The
    ///     channel serializes calls, so the test is observable: each completed query strictly
    ///     increments either successes or failures by one.
    /// </summary>
    [Fact]
    public async Task GetHealthSnapshot_ConcurrentCallsAndReads_CountersAreInternallyConsistent()
    {
        var pipe = UniquePipeName();
        await using var server = new FakeSidecarServer(pipe, Secret)
        {
            OnReadRaw = _ => new ReadRawReply { Success = true },
        };
        await server.StartAsync();

        await using var client = new WonderwareHistorianClient(OptsFor(pipe));

        using var stop = new CancellationTokenSource();
        var readerSawInconsistent = false;

#pragma warning disable xUnit1051 // Internal Task.Run loop drives a polling stress test; cancellation flows via stop.IsCancellationRequested below.
        var reader = Task.Run(() =>
        {
            while (!stop.IsCancellationRequested)
            {
                var snap = client.GetHealthSnapshot();
                // Every completed call increments TotalQueries AND exactly one of
                // TotalSuccesses or TotalFailures under the same lock; an in-flight call
                // has not yet incremented any of them. So TotalQueries should always equal
                // the sum of TotalSuccesses + TotalFailures (no in-between state visible).
                if (snap.TotalSuccesses + snap.TotalFailures != snap.TotalQueries)
                {
                    readerSawInconsistent = true;
                }
            }
        });
#pragma warning restore xUnit1051

        for (var i = 0; i < 50; i++)
        {
            await client.ReadRawAsync("Tag", DateTime.UtcNow, DateTime.UtcNow, 1, TestContext.Current.CancellationToken);
        }

        stop.Cancel();
        await reader;

        readerSawInconsistent.ShouldBeFalse(
            "GetHealthSnapshot exposed TotalQueries that disagreed with the sum of TotalSuccesses + TotalFailures — counters are not updated under a single lock.");

        var final = client.GetHealthSnapshot();
        final.TotalQueries.ShouldBe(50);
        final.TotalSuccesses.ShouldBe(50);
        final.TotalFailures.ShouldBe(0);
    }
}
