using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;

namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;

/// <summary>
/// Site-side hosted service (S2/UA5) that periodically reads the process-wide
/// <see cref="ScriptExecutionScheduler"/> gauges (queue depth, busy thread count,
/// oldest in-flight script age) and pushes them into
/// <see cref="ISiteHealthCollector.SetScriptSchedulerStats"/> so the next
/// <see cref="ISiteHealthCollector.CollectReport"/> carries a fresh snapshot on
/// the site health report. Mirrors <c>SiteEventLogFailureCountReporter</c>:
/// immediate first probe, fixed cadence, exceptions logged and swallowed so the
/// loop survives every probe failure.
/// </summary>
public sealed class ScriptSchedulerStatsReporter : BackgroundService
{
    /// <summary>Default poll cadence (10 s) — coarse enough to amortise across health reports.</summary>
    internal static readonly TimeSpan DefaultPollInterval = TimeSpan.FromSeconds(10);

    private readonly ISiteHealthCollector _collector;
    private readonly SiteRuntimeOptions _options;
    private readonly ILogger<ScriptSchedulerStatsReporter> _logger;
    private readonly TimeSpan _pollInterval;

    /// <summary>
    /// Script-execution scheduler seam (#18): the process-wide scheduler when null, or
    /// an injected instance so the reporter samples the same pool the actors run on.
    /// </summary>
    private readonly ScriptExecutionScheduler? _scheduler;

    /// <summary>Initializes a new instance of <see cref="ScriptSchedulerStatsReporter"/>.</summary>
    /// <param name="collector">The site health collector that receives the scheduler gauges.</param>
    /// <param name="options">Site runtime options supplying the shared scheduler's thread count.</param>
    /// <param name="logger">Logger instance.</param>
    /// <param name="pollInterval">Poll interval override; defaults to <see cref="DefaultPollInterval"/> (10 s).</param>
    /// <param name="scheduler">Optional script-execution scheduler override (#18); null samples the process-wide shared scheduler.</param>
    public ScriptSchedulerStatsReporter(
        ISiteHealthCollector collector,
        SiteRuntimeOptions options,
        ILogger<ScriptSchedulerStatsReporter> logger,
        TimeSpan? pollInterval = null,
        ScriptExecutionScheduler? scheduler = null)
    {
        _collector = collector ?? throw new ArgumentNullException(nameof(collector));
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _pollInterval = pollInterval ?? DefaultPollInterval;
        _scheduler = scheduler;
    }

    /// <inheritdoc />
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Immediate first probe so the first health report after start carries a
        // real snapshot instead of zeros.
        Probe();

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(_pollInterval, stoppingToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                break;
            }

            Probe();
        }
    }

    private void Probe()
    {
        try
        {
            var scheduler = _scheduler ?? ScriptExecutionScheduler.Shared(_options);
            _collector.SetScriptSchedulerStats(
                scheduler.QueueDepth,
                scheduler.BusyThreadCount,
                scheduler.OldestBusyAge?.TotalSeconds,
                // WP3.1: workers detached and replaced by the stuck-script watchdog.
                scheduler.DetachedThreadCount);
        }
        catch (Exception ex)
        {
            // Catch-all is deliberate: the hosted service must survive every class
            // of probe failure so the next tick gets a chance.
            _logger.LogWarning(ex, "ScriptSchedulerStatsReporter probe failed; next tick will retry.");
        }
    }
}
