using ScadaLink.Commons.Types.Enums;

namespace ScadaLink.HealthMonitoring.Tests;

public class SiteHealthCollectorTests
{
    private readonly SiteHealthCollector _collector = new();

    [Fact]
    public void CollectReport_ReturnsZeroCounters_WhenNoErrorsRecorded()
    {
        var report = _collector.CollectReport("site-1");

        Assert.Equal("site-1", report.SiteId);
        Assert.Equal(0, report.ScriptErrorCount);
        Assert.Equal(0, report.AlarmEvaluationErrorCount);
        Assert.Equal(0, report.DeadLetterCount);
    }

    [Fact]
    public void IncrementScriptError_AccumulatesBetweenReports()
    {
        _collector.IncrementScriptError();
        _collector.IncrementScriptError();
        _collector.IncrementScriptError();

        var report = _collector.CollectReport("site-1");
        Assert.Equal(3, report.ScriptErrorCount);
    }

    [Fact]
    public void IncrementAlarmError_AccumulatesBetweenReports()
    {
        _collector.IncrementAlarmError();
        _collector.IncrementAlarmError();

        var report = _collector.CollectReport("site-1");
        Assert.Equal(2, report.AlarmEvaluationErrorCount);
    }

    [Fact]
    public void IncrementDeadLetter_AccumulatesBetweenReports()
    {
        _collector.IncrementDeadLetter();

        var report = _collector.CollectReport("site-1");
        Assert.Equal(1, report.DeadLetterCount);
    }

    [Fact]
    public void CollectReport_ResetsCounters_AfterCollection()
    {
        _collector.IncrementScriptError();
        _collector.IncrementAlarmError();
        _collector.IncrementDeadLetter();

        var first = _collector.CollectReport("site-1");
        Assert.Equal(1, first.ScriptErrorCount);
        Assert.Equal(1, first.AlarmEvaluationErrorCount);
        Assert.Equal(1, first.DeadLetterCount);

        var second = _collector.CollectReport("site-1");
        Assert.Equal(0, second.ScriptErrorCount);
        Assert.Equal(0, second.AlarmEvaluationErrorCount);
        Assert.Equal(0, second.DeadLetterCount);
    }

    [Fact]
    public void UpdateConnectionHealth_ReflectedInReport()
    {
        _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected);
        _collector.UpdateConnectionHealth("opc-2", ConnectionHealth.Disconnected);

        var report = _collector.CollectReport("site-1");

        Assert.Equal(2, report.DataConnectionStatuses.Count);
        Assert.Equal(ConnectionHealth.Connected, report.DataConnectionStatuses["opc-1"]);
        Assert.Equal(ConnectionHealth.Disconnected, report.DataConnectionStatuses["opc-2"]);
    }

    [Fact]
    public void ConnectionHealth_NotResetAfterCollect()
    {
        _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected);

        _collector.CollectReport("site-1");
        var second = _collector.CollectReport("site-1");

        Assert.Single(second.DataConnectionStatuses);
        Assert.Equal(ConnectionHealth.Connected, second.DataConnectionStatuses["opc-1"]);
    }

    [Fact]
    public void RemoveConnection_RemovesFromReport()
    {
        _collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected);
        _collector.UpdateTagResolution("opc-1", 10, 8);
        _collector.RemoveConnection("opc-1");

        var report = _collector.CollectReport("site-1");
        Assert.Empty(report.DataConnectionStatuses);
        Assert.Empty(report.TagResolutionCounts);
    }

    [Fact]
    public void UpdateTagResolution_ReflectedInReport()
    {
        _collector.UpdateTagResolution("opc-1", 50, 45);

        var report = _collector.CollectReport("site-1");

        Assert.Single(report.TagResolutionCounts);
        Assert.Equal(50, report.TagResolutionCounts["opc-1"].TotalSubscribed);
        Assert.Equal(45, report.TagResolutionCounts["opc-1"].SuccessfullyResolved);
    }

    [Fact]
    public void StoreAndForwardBufferDepths_IsEmptyPlaceholder()
    {
        var report = _collector.CollectReport("site-1");
        Assert.Empty(report.StoreAndForwardBufferDepths);
    }

    [Fact]
    public void CollectReport_IncludesUtcTimestamp()
    {
        var before = DateTimeOffset.UtcNow;
        var report = _collector.CollectReport("site-1");
        var after = DateTimeOffset.UtcNow;

        Assert.InRange(report.ReportTimestamp, before, after);
    }

    [Fact]
    public void CollectReport_SequenceNumberIsZero_CallerAssignsIt()
    {
        var report = _collector.CollectReport("site-1");
        Assert.Equal(0, report.SequenceNumber);
    }

    [Fact]
    public async Task ThreadSafety_ConcurrentIncrements()
    {
        const int iterations = 10_000;
        var tasks = new[]
        {
            Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementScriptError(); }),
            Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementAlarmError(); }),
            Task.Run(() => { for (int i = 0; i < iterations; i++) _collector.IncrementDeadLetter(); })
        };

        await Task.WhenAll(tasks);

        var report = _collector.CollectReport("site-1");
        Assert.Equal(iterations, report.ScriptErrorCount);
        Assert.Equal(iterations, report.AlarmEvaluationErrorCount);
        Assert.Equal(iterations, report.DeadLetterCount);
    }
}
