namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;

/// <summary>
/// Tuning knobs for the central <see cref="SiteAuditReconciliationActor"/> singleton.
/// Defaults: pull every 5 minutes per site, 256 rows per
/// batch, declare a site "stalled" after two consecutive pull cycles return non-empty
/// AND <c>MoreAvailable=true</c> (the backlog is not draining).
/// </summary>
/// <remarks>
/// <para>
/// The reconciliation actor is the fallback when push telemetry is
/// lost; it is intentionally low-frequency. Lowering
/// <see cref="ReconciliationIntervalSeconds"/> in production trades MS SQL load for
/// fresher self-healing — keep the default unless a deployment can prove the extra
/// load is acceptable.
/// </para>
/// <para>
/// <see cref="StalledAfterNonDrainingCycles"/> = 2 because a single non-draining
/// cycle can happen on a surge (e.g. a backed-up site replays its hot queue); the
/// stalled signal should only fire when the backlog persists across cycles, which is
/// the symptom the central health surface is asking us to detect.
/// </para>
/// </remarks>
public sealed class SiteAuditReconciliationOptions
{
    /// <summary>
    /// Period of the reconciliation tick. Each tick visits every known site once.
    /// </summary>
    public int ReconciliationIntervalSeconds { get; set; } = 300;

    /// <summary>
    /// Test-only override for finer control over the tick cadence than
    /// whole-second resolution allows. When non-null, takes precedence over
    /// <see cref="ReconciliationIntervalSeconds"/> AND bypasses the
    /// <see cref="ReconciliationInterval"/> minimum clamp (so tests can use
    /// millisecond cadences). Production config exposes
    /// <see cref="ReconciliationIntervalSeconds"/> only and never sets this
    /// knob — but because the options class is <c>Bind</c>-ed wholesale, a
    /// config value at <c>AuditLog:Reconciliation:ReconciliationIntervalOverride</c>
    /// WOULD bind if present; operators must not set it.
    /// </summary>
    public TimeSpan? ReconciliationIntervalOverride { get; set; }

    /// <summary>
    /// Minimum interval the config-bound <see cref="ReconciliationIntervalSeconds"/>
    /// can resolve to. Clamps a misconfigured <c>ReconciliationIntervalSeconds: 0</c>
    /// (or a negative value) away from <see cref="TimeSpan.Zero"/>, which would make
    /// Akka's <c>ScheduleTellRepeatedlyCancelable</c> spin. The test-only
    /// <see cref="ReconciliationIntervalOverride"/> bypasses this clamp so unit tests
    /// can still drop the cadence to milliseconds.
    /// </summary>
    private static readonly TimeSpan MinConfiguredInterval = TimeSpan.FromSeconds(1);

    /// <summary>
    /// Resolves the effective tick interval, honouring the test override when
    /// set. Falls back to <see cref="ReconciliationIntervalSeconds"/>, clamped to at
    /// least <see cref="MinConfiguredInterval"/> so a zero/negative config value can
    /// never yield <see cref="TimeSpan.Zero"/> (which would spin the scheduler).
    /// </summary>
    public TimeSpan ReconciliationInterval
    {
        get
        {
            if (ReconciliationIntervalOverride is { } overrideValue)
            {
                return overrideValue;
            }

            var resolved = TimeSpan.FromSeconds(ReconciliationIntervalSeconds);
            return resolved < MinConfiguredInterval ? MinConfiguredInterval : resolved;
        }
    }

    /// <summary>
    /// Maximum number of <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.AuditEvent"/>
    /// rows requested in a single <c>PullAuditEvents</c> RPC call.
    /// </summary>
    public int BatchSize { get; set; } = 256;

    /// <summary>
    /// Number of consecutive non-draining cycles (events returned AND
    /// <c>MoreAvailable=true</c>) that must accumulate for a site before the actor
    /// publishes <c>SiteAuditTelemetryStalledChanged(Stalled: true)</c> on the
    /// EventStream.
    /// </summary>
    public int StalledAfterNonDrainingCycles { get; set; } = 2;
}
