using Akka.TestKit.Xunit2;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Streaming;

namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Streaming;

/// <summary>
/// WP2.6d: the hosted reporter must lift <see cref="SiteStreamManager.AlarmPublishDroppedCount"/>
/// onto the site health report via <see cref="ISiteHealthCollector"/>. Uses the real
/// collector (mirrors <c>ScriptSchedulerStatsReporterTests</c> — NSubstitute is not
/// referenced by this test project). The queue's own drop-counting mechanism is verified
/// deterministically and separately by
/// <see cref="Streaming.SiteStreamManagerTests.CreateAlarmPublishQueue_OverCapacity_DropsOldestAndInvokesCallback"/>;
/// this test proves the reporter's poll-and-push wiring runs correctly.
/// </summary>
public class SiteStreamAlarmDropReporterTests : TestKit, IDisposable
{
    void IDisposable.Dispose() => Shutdown();

    [Fact]
    public async Task Reporter_PushesAlarmDropCountToCollector()
    {
        var options = new SiteRuntimeOptions { StreamBufferSize = 100 };
        var collector = new SiteHealthCollector();
        var streamManager = new SiteStreamManager(options, NullLogger<SiteStreamManager>.Instance);
        streamManager.Initialize(Sys);

        using var reporter = new SiteStreamAlarmDropReporter(
            collector, streamManager, NullLogger<SiteStreamAlarmDropReporter>.Instance,
            pollInterval: TimeSpan.FromMilliseconds(50));

        await reporter.StartAsync(CancellationToken.None);
        try
        {
            // The immediate first probe (no drops yet) must reach the report as 0 —
            // proving the reporter actually ran and pushed a value, not that the field
            // simply defaulted.
            await WaitUntilAsync(() =>
                collector.CollectReport("site-1").SiteStreamAlarmDropCount == streamManager.AlarmPublishDroppedCount);

            var report = collector.CollectReport("site-1");
            Assert.Equal(streamManager.AlarmPublishDroppedCount, report.SiteStreamAlarmDropCount);
        }
        finally
        {
            await reporter.StopAsync(CancellationToken.None);
        }
    }

    private static async Task WaitUntilAsync(Func<bool> condition)
    {
        for (var i = 0; i < 100 && !condition(); i++)
            await Task.Delay(50);
        Assert.True(condition(), "condition not met within timeout");
    }
}
