using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ScadaLink.HealthMonitoring;

/// <summary>
/// Central-side counterpart to <see cref="HealthReportSender"/>.
/// Periodically builds a SiteHealthReport for the central cluster itself
/// (siteId = <see cref="CentralSiteId"/>) and feeds it into the local
/// CentralHealthAggregator so the UI can render central as another card
/// on /monitoring/health. Only the cluster leader (Primary) generates
/// reports — the standby's aggregator catches up on failover when it
/// becomes Primary and starts its own loop.
/// </summary>
public class CentralHealthReportLoop : BackgroundService
{
    /// <summary>
    /// Reserved siteId used to represent the central cluster in the
    /// shared CentralHealthAggregator keyspace.
    /// </summary>
    public const string CentralSiteId = "central";

    private readonly ISiteHealthCollector _collector;
    private readonly ICentralHealthAggregator _aggregator;
    private readonly IClusterNodeProvider _clusterNodeProvider;
    private readonly HealthMonitoringOptions _options;
    private readonly ILogger<CentralHealthReportLoop> _logger;

    // Seeded with Unix-ms so reports from a newly-elected central leader
    // always sort after reports from any prior leader for siteId="central".
    private long _sequenceNumber = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

    public CentralHealthReportLoop(
        ISiteHealthCollector collector,
        ICentralHealthAggregator aggregator,
        IClusterNodeProvider clusterNodeProvider,
        IOptions<HealthMonitoringOptions> options,
        ILogger<CentralHealthReportLoop> logger)
    {
        _collector = collector;
        _aggregator = aggregator;
        _clusterNodeProvider = clusterNodeProvider;
        _options = options.Value;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation(
            "Central health report loop starting, interval {Interval}s",
            _options.ReportInterval.TotalSeconds);

        using var timer = new PeriodicTimer(_options.ReportInterval);

        while (await timer.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
        {
            try
            {
                var isPrimary = _clusterNodeProvider.SelfIsPrimary;
                _collector.SetActiveNode(isPrimary);

                if (!isPrimary)
                    continue;

                _collector.SetClusterNodes(_clusterNodeProvider.GetClusterNodes());

                var seq = Interlocked.Increment(ref _sequenceNumber);
                var report = _collector.CollectReport(CentralSiteId);
                var reportWithSeq = report with { SequenceNumber = seq };

                _aggregator.ProcessReport(reportWithSeq);

                _logger.LogDebug("Generated central health report #{Seq}", seq);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to generate central health report");
            }
        }
    }
}
