namespace ZB.MOM.WW.ScadaBridge.SiteCallAudit;
///
/// Configuration options for the Site Call Audit: stuck-call detection +
/// KPI windowing for the read-side, plus the cadence/retention knobs for the
/// two central-singleton schedulers — the periodic per-site reconciliation
/// pull (self-heal for lost telemetry) and the daily terminal-row purge.
/// Mirrors the KPI-relevant subset of NotificationOutboxOptions and the
/// scheduler-cadence shape of SiteAuditReconciliationOptions /
/// AuditLogPurgeOptions.
///
public class SiteCallAuditOptions
{
///
/// Age past which a non-terminal cached call (Pending/Retrying)
/// is considered stuck. Display-only — surfaced as the Stuck KPI and a row
/// badge, with no escalation. Default 10 minutes, matching
/// NotificationOutboxOptions.StuckAgeThreshold.
///
public TimeSpan StuckAgeThreshold { get; set; } = TimeSpan.FromMinutes(10);
///
/// Trailing window used to compute the delivered- and failed-last-interval
/// throughput KPIs. Default 1 minute, matching
/// NotificationOutboxOptions.DeliveredKpiWindow.
///
public TimeSpan KpiInterval { get; set; } = TimeSpan.FromMinutes(1);
///
/// Ask timeout for the central→site Retry/Discard relay. When
/// the owning site does not ack a RetryParkedOperation /
/// DiscardParkedOperation within this window — site offline, no
/// route to the site, or central buffering deliberately absent — the relay
/// reports a SiteUnreachable outcome. Default 10 seconds: long enough
/// to absorb a healthy cross-cluster round-trip, short enough that an
/// operator clicking Retry on an offline site gets a fast, honest answer.
///
/// Ordering invariant: RelayTimeout must stay below
/// CommunicationOptions.QueryTimeout (default 30s), the timeout the
/// outer CommunicationService.RetrySiteCallAsync/DiscardSiteCallAsync
/// Ask of the SiteCallAuditActor uses. The outer Ask must outlive this
/// inner site relay Ask so the inner relay times out first and yields the
/// distinct SiteUnreachable outcome; if the outer Ask expired first,
/// that outcome would be lost to a generic Ask-timeout exception. The
/// defaults (10s < 30s) satisfy this — keep the gap when tuning either.
///
///
public TimeSpan RelayTimeout { get; set; } = TimeSpan.FromSeconds(10);
// ── Reconciliation tick: periodic per-site self-heal pull ──
///
/// Period of the reconciliation tick. Each tick visits every known site
/// once, pulls changed SiteCall rows since a per-site cursor, and
/// upserts them idempotently — the documented self-heal when best-effort
/// push telemetry is lost. Default 5 minutes, matching the sibling
/// SiteAuditReconciliationOptions cadence. Clamped to at least
/// via .
///
public TimeSpan ReconciliationInterval { get; set; } = TimeSpan.FromMinutes(5);
///
/// Test-only override for the reconciliation tick cadence — bypasses the
/// clamp so unit tests can drop the
/// cadence to milliseconds. Production config never sets this; leave null.
///
public TimeSpan? ReconciliationIntervalOverride { get; set; }
///
/// Maximum number of SiteCall rows requested per PullSiteCalls
/// RPC. Default 500. A MoreAvailable=true response signals the cursor
/// advanced and the next tick should keep draining the backlog.
///
public int ReconciliationBatchSize { get; set; } = 500;
///
/// Minimum interval the config-bound can
/// resolve to. Clamps a misconfigured 0 (or negative) value away from
/// , which would make Akka's
/// ScheduleTellRepeatedlyCancelable spin — the exact footgun flagged in
/// a prior review of the sibling reconciliation options.
///
private static readonly TimeSpan MinReconciliationInterval = TimeSpan.FromSeconds(1);
///
/// Resolves the effective reconciliation tick interval: the test override
/// when set (bypassing the clamp), otherwise
/// clamped to at least so a
/// zero/negative config value can never yield .
///
public TimeSpan ResolvedReconciliationInterval =>
ReconciliationIntervalOverride is { } o
? o
: ReconciliationInterval < MinReconciliationInterval
? MinReconciliationInterval
: ReconciliationInterval;
// ── Purge scheduler: daily terminal-row purge ──
///
/// Period of the purge tick. Each tick drops terminal SiteCalls rows
/// older than the retention window via
/// .
/// Default 24 hours, matching AuditLogPurgeOptions. Clamped to at
/// least via .
///
public TimeSpan PurgeInterval { get; set; } = TimeSpan.FromHours(24);
///
/// Test-only override for the purge tick cadence — bypasses the
/// clamp so unit tests can drop the cadence
/// to milliseconds. Production config never sets this; leave null.
///
public TimeSpan? PurgeIntervalOverride { get; set; }
///
/// Retention window for terminal rows. On each purge tick a row whose
/// TerminalAtUtc is older than UtcNow - RetentionDays is
/// deleted; non-terminal rows are never purged. Default 365 days, matching
/// the central audit-store retention policy.
///
public int RetentionDays { get; set; } = 365;
///
/// Minimum interval the config-bound can resolve
/// to. Clamps a misconfigured 0 (or negative) value away from
/// for the same scheduler-spin reason as
/// ; the purge is daily so the floor
/// is a more generous 1 minute.
///
private static readonly TimeSpan MinPurgeInterval = TimeSpan.FromMinutes(1);
///
/// Resolves the effective purge tick interval: the test override when set
/// (bypassing the clamp), otherwise clamped to at
/// least .
///
public TimeSpan ResolvedPurgeInterval =>
PurgeIntervalOverride is { } o
? o
: PurgeInterval < MinPurgeInterval
? MinPurgeInterval
: PurgeInterval;
}