namespace ZB.MOM.WW.ScadaBridge.Communication;
///
/// Configuration options for central-site communication, including per-pattern
/// timeouts and transport heartbeat settings.
///
public class CommunicationOptions
{
/// Timeout for deployment commands (typically longest due to apply logic).
public TimeSpan DeploymentTimeout { get; set; } = TimeSpan.FromMinutes(2);
/// Timeout for lifecycle commands (disable, enable, delete).
public TimeSpan LifecycleTimeout { get; set; } = TimeSpan.FromSeconds(30);
/// Timeout for artifact deployment commands.
public TimeSpan ArtifactDeploymentTimeout { get; set; } = TimeSpan.FromMinutes(1);
/// Timeout for remote query requests (event logs, parked messages).
public TimeSpan QueryTimeout { get; set; } = TimeSpan.FromSeconds(30);
/// Timeout for integration call routing.
public TimeSpan IntegrationTimeout { get; set; } = TimeSpan.FromSeconds(30);
/// Timeout for debug view subscribe/unsubscribe handshake.
public TimeSpan DebugViewTimeout { get; set; } = TimeSpan.FromSeconds(10);
/// Timeout for health report acknowledgement (fire-and-forget, but bounded).
public TimeSpan HealthReportTimeout { get; set; } = TimeSpan.FromSeconds(10);
///
/// Notification Outbox: timeout for forwarding a buffered notification to central
/// and awaiting its NotificationSubmitAck. A timeout is treated as a
/// transient failure — the Store-and-Forward engine keeps the message buffered
/// and retries the forward at the fixed retry interval.
///
public TimeSpan NotificationForwardTimeout { get; set; } = TimeSpan.FromSeconds(30);
///
/// Contact point addresses for the central cluster (e.g. "akka.tcp://scadabridge@central-a:8081").
/// Used by site nodes to create a ClusterClient for reaching central.
///
public List CentralContactPoints { get; set; } = new();
/// gRPC keepalive ping interval for streaming connections.
public TimeSpan GrpcKeepAlivePingDelay { get; set; } = TimeSpan.FromSeconds(15);
/// gRPC keepalive ping timeout — stream is considered dead if no response within this period.
public TimeSpan GrpcKeepAlivePingTimeout { get; set; } = TimeSpan.FromSeconds(10);
/// Maximum lifetime for a single gRPC stream before the server forces re-establishment.
public TimeSpan GrpcMaxStreamLifetime { get; set; } = TimeSpan.FromHours(4);
/// Maximum number of concurrent gRPC streaming subscriptions per site node.
public int GrpcMaxConcurrentStreams { get; set; } = 100;
/// Akka.Remote transport heartbeat interval.
public TimeSpan TransportHeartbeatInterval { get; set; } = TimeSpan.FromSeconds(5);
///
/// Application-level site→central heartbeat cadence. Distinct from
/// (the Akka.Remote failure-detector
/// setting) — tuning the transport FD must not silently retune the health
/// heartbeat. Default equals the old effective value (5s) — no behavior change.
///
public TimeSpan ApplicationHeartbeatInterval { get; set; } = TimeSpan.FromSeconds(5);
/// Akka.Remote transport failure detection threshold.
public TimeSpan TransportFailureThreshold { get; set; } = TimeSpan.FromSeconds(15);
///
/// Base URL (Traefik/LB) the SITE uses to fetch deploy configs from central,
/// e.g. "https://central.example:9000". Carried in RefreshDeploymentCommand so
/// sites need no new standing config. Empty disables notify-and-fetch fallback.
///
public string CentralFetchBaseUrl { get; set; } = "";
///
/// How long a staged PendingDeployment (and its fetch token) stays valid. Must
/// comfortably cover both site nodes' fetches within one deploy window.
///
public TimeSpan PendingDeploymentTtl { get; set; } = TimeSpan.FromMinutes(5);
///
/// How often the central PendingDeploymentPurgeActor singleton reclaims
/// expired (TTL-elapsed) PendingDeployment staging rows. Best-effort hygiene only:
/// supersession bounds pending rows to ≤1 per instance and the config-fetch endpoint
/// already enforces the TTL, so this purge merely sweeps rows left behind by instances
/// that are deployed once and never re-deployed. Default 1 hour ≫ .
///
public TimeSpan PendingDeploymentPurgeInterval { get; set; } = TimeSpan.FromHours(1);
// ── Aggregated live alarm cache (plan #10, Task 4) ───────────────────────────
// Introduced by Task 4 with sane defaults; Task 6 formalizes eager validation
// (positive interval/linger, positive concurrency/subscriber cap) and telemetry.
///
/// How long the shared per-site live alarm aggregator keeps its gRPC stream +
/// in-memory cache warm after its LAST Alarm Summary viewer leaves, before it is
/// torn down. A short linger avoids re-seed thrash when an operator navigates away
/// and straight back. Default 30s.
///
public TimeSpan LiveAlarmCacheLinger { get; set; } = TimeSpan.FromSeconds(30);
///
/// Cadence of the per-site aggregator's periodic reconcile snapshot fan-out. The
/// backstop that corrects instance-set drift (enable/disable/deploy/delete) and any
/// alarm delta missed by the live stream, since the aggregated stream never carries
/// an explicit "removed" event. Default 60s.
///
public TimeSpan LiveAlarmCacheReconcileInterval { get; set; } = TimeSpan.FromSeconds(60);
///
/// Max concurrent per-instance snapshot fetches during a live-cache seed/reconcile
/// fan-out — the aggregated analogue of the Alarm Summary poll's
/// MaxConcurrentFetches. Default 8.
///
public int LiveAlarmCacheSeedConcurrency { get; set; } = 8;
///
/// Upper bound on concurrent Alarm Summary viewers sharing one site's aggregator.
/// A defensive cap only — one gRPC stream per site is shared regardless of viewer
/// count; this just bounds the subscriber list. Default 200.
///
public int LiveAlarmCacheMaxSubscribersPerSite { get; set; } = 200;
///
/// Publish-coalescing window for live alarm deltas: an applied delta marks the cache
/// dirty and one publish (fresh snapshot + per-viewer onChanged fan-out) fires after
/// this window, batching an alarm storm into ~4 publishes/second instead of one per
/// transition (review 02 round 2, N6). Zero = publish per delta (legacy). Seed and
/// reconcile publishes are always immediate. Default 250 ms.
///
public TimeSpan LiveAlarmCachePublishCoalesce { get; set; } = TimeSpan.FromMilliseconds(250);
}