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

/// <summary>
/// Tuning knobs for the central <see cref="AuditLogPurgeActor"/> singleton.
/// Default cadence is 24 hours per the M6 plan; the retention window itself
/// is sourced from <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Configuration.AuditLogOptions.RetentionDays"/>
/// (default 365) so operators tune retention from a single section.
/// </summary>
/// <remarks>
/// <para>
/// The purge actor is a daily-cadence singleton, not a hot-loop, because
/// partition-switch I/O is metadata-only but the drop-and-rebuild dance
/// briefly removes the <c>UX_AuditLog_EventId</c> unique index — running
/// more often than necessary trades index-rebuild outages for marginal
/// freshness gains. Lower this only when an operator can prove they need
/// sub-daily purge granularity.
/// </para>
/// <para>
/// <see cref="IntervalOverride"/> exists for tests to drop the cadence to
/// milliseconds; production config is expected to set <see cref="IntervalHours"/>
/// only. Because this options class is <c>Bind</c>-ed wholesale, a config value
/// at <c>AuditLog:Purge:IntervalOverride</c> would bind if present (and would
/// bypass the <see cref="Interval"/> minimum clamp) — operators must not set it.
/// </para>
/// </remarks>
public sealed class AuditLogPurgeOptions
{
    /// <summary>Period of the purge tick in hours (default 24).</summary>
    public int IntervalHours { get; set; } = 24;

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

    /// <summary>
    /// Minimum interval the config-bound <see cref="IntervalHours"/> can
    /// resolve to. Clamps a misconfigured <c>IntervalHours: 0</c> (or a
    /// negative value) away from <see cref="TimeSpan.Zero"/> — a zero
    /// interval would make Akka's <c>ScheduleTellRepeatedlyCancelable</c>
    /// spin, looping the partition drop/rebuild dance into a sustained SQL
    /// outage. The test-only <see cref="IntervalOverride"/> bypasses this
    /// clamp so unit tests can still drop the cadence to milliseconds.
    /// </summary>
    private static readonly TimeSpan MinConfiguredInterval = TimeSpan.FromMinutes(1);

    /// <summary>
    /// Resolves the effective tick interval, honouring the test override
    /// when set. Falls back to <see cref="IntervalHours"/>, 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 Interval
    {
        get
        {
            if (IntervalOverride is { } overrideValue)
            {
                return overrideValue;
            }

            var resolved = TimeSpan.FromHours(IntervalHours);
            return resolved < MinConfiguredInterval ? MinConfiguredInterval : resolved;
        }
    }
}
