using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ScadaLink.Commons.Messages.Health;
using ScadaLink.Commons.Types.Enums;
using ScadaLink.HealthMonitoring;

namespace ScadaLink.PerformanceTests;

/// <summary>
/// WP-4 (Phase 8): Performance test framework for health reporting aggregation.
/// Verifies health reporting from 10 sites can be aggregated correctly.
/// </summary>
public class HealthAggregationTests
{
    private readonly CentralHealthAggregator _aggregator;

    public HealthAggregationTests()
    {
        var options = Options.Create(new HealthMonitoringOptions
        {
            ReportInterval = TimeSpan.FromSeconds(30),
            OfflineTimeout = TimeSpan.FromSeconds(60)
        });
        _aggregator = new CentralHealthAggregator(
            options,
            NullLogger<CentralHealthAggregator>.Instance);
    }

    [Trait("Category", "Performance")]
    [Fact]
    public void AggregateHealthReports_10Sites_AllTracked()
    {
        const int siteCount = 10;

        for (var i = 0; i < siteCount; i++)
        {
            var siteId = $"site-{i + 1:D2}";
            var report = new SiteHealthReport(
                SiteId: siteId,
                SequenceNumber: 1,
                ReportTimestamp: DateTimeOffset.UtcNow,
                DataConnectionStatuses: new Dictionary<string, ConnectionHealth>
                {
                    [$"opc-{siteId}"] = ConnectionHealth.Connected
                },
                TagResolutionCounts: new Dictionary<string, TagResolutionStatus>
                {
                    [$"opc-{siteId}"] = new(75, 72)
                },
                ScriptErrorCount: 0,
                AlarmEvaluationErrorCount: 0,
                StoreAndForwardBufferDepths: new Dictionary<string, int>
                {
                    ["ext-system"] = i * 2
                },
                DeadLetterCount: 0,
                DeployedInstanceCount: 0,
                EnabledInstanceCount: 0,
                DisabledInstanceCount: 0);

            _aggregator.ProcessReport(report);
        }

        var states = _aggregator.GetAllSiteStates();
        Assert.Equal(siteCount, states.Count);
        Assert.All(states.Values, s => Assert.True(s.IsOnline));
    }

    [Trait("Category", "Performance")]
    [Fact]
    public void AggregateHealthReports_RapidUpdates_HandlesVolume()
    {
        const int siteCount = 10;
        const int updatesPerSite = 100;

        for (var seq = 1; seq <= updatesPerSite; seq++)
        {
            for (var s = 0; s < siteCount; s++)
            {
                var report = new SiteHealthReport(
                    SiteId: $"site-{s + 1:D2}",
                    SequenceNumber: seq,
                    ReportTimestamp: DateTimeOffset.UtcNow,
                    DataConnectionStatuses: new Dictionary<string, ConnectionHealth>(),
                    TagResolutionCounts: new Dictionary<string, TagResolutionStatus>(),
                    ScriptErrorCount: seq % 5 == 0 ? 1 : 0,
                    AlarmEvaluationErrorCount: 0,
                    StoreAndForwardBufferDepths: new Dictionary<string, int>(),
                    DeadLetterCount: 0,
                DeployedInstanceCount: 0,
                EnabledInstanceCount: 0,
                DisabledInstanceCount: 0);

                _aggregator.ProcessReport(report);
            }
        }

        var states = _aggregator.GetAllSiteStates();
        Assert.Equal(siteCount, states.Count);

        // Verify all sites have the latest sequence number
        Assert.All(states.Values, s =>
        {
            Assert.Equal(updatesPerSite, s.LastSequenceNumber);
            Assert.True(s.IsOnline);
        });
    }

    [Trait("Category", "Performance")]
    [Fact]
    public void AggregateHealthReports_StaleReportsRejected()
    {
        var siteId = "site-01";

        // Send report with seq 10
        _aggregator.ProcessReport(new SiteHealthReport(
            siteId, 10, DateTimeOffset.UtcNow,
            new Dictionary<string, ConnectionHealth>(),
            new Dictionary<string, TagResolutionStatus>(),
            5, 0, new Dictionary<string, int>(), 0, 0, 0, 0));

        // Send stale report with seq 5 — should be rejected
        _aggregator.ProcessReport(new SiteHealthReport(
            siteId, 5, DateTimeOffset.UtcNow,
            new Dictionary<string, ConnectionHealth>(),
            new Dictionary<string, TagResolutionStatus>(),
            99, 0, new Dictionary<string, int>(), 0, 0, 0, 0));

        var state = _aggregator.GetSiteState(siteId);
        Assert.NotNull(state);
        Assert.Equal(10, state!.LastSequenceNumber);
        // The script error count from report 10 (5) should be kept, not replaced by 99
        Assert.Equal(5, state.LatestReport!.ScriptErrorCount);
    }

    [Trait("Category", "Performance")]
    [Fact]
    public void HealthCollector_CollectReport_ResetsIntervalCounters()
    {
        var collector = new SiteHealthCollector();

        // Simulate errors during an interval
        for (var i = 0; i < 10; i++) collector.IncrementScriptError();
        for (var i = 0; i < 3; i++) collector.IncrementAlarmError();
        for (var i = 0; i < 7; i++) collector.IncrementDeadLetter();

        collector.UpdateConnectionHealth("opc-1", ConnectionHealth.Connected);
        collector.UpdateTagResolution("opc-1", 75, 72);

        var report = collector.CollectReport("site-01");

        Assert.Equal("site-01", report.SiteId);
        Assert.Equal(10, report.ScriptErrorCount);
        Assert.Equal(3, report.AlarmEvaluationErrorCount);
        Assert.Equal(7, report.DeadLetterCount);
        Assert.Single(report.DataConnectionStatuses);

        // Second collect should have reset interval counters
        var report2 = collector.CollectReport("site-01");
        Assert.Equal(0, report2.ScriptErrorCount);
        Assert.Equal(0, report2.AlarmEvaluationErrorCount);
        Assert.Equal(0, report2.DeadLetterCount);
        // Connection status persists (not interval-based)
        Assert.Single(report2.DataConnectionStatuses);
    }
}
