namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer;

/// <summary>
/// Configuration options for the Data Connection Layer.
/// </summary>
public class DataConnectionOptions
{
    /// <summary>Fixed interval between reconnect attempts after disconnect.</summary>
    public TimeSpan ReconnectInterval { get; set; } = TimeSpan.FromSeconds(5);

    /// <summary>
    /// Floor interval for retrying failed tag path resolution. The retry backs off
    /// exponentially (doubling per fully-failed round) up to
    /// <see cref="TagResolutionRetryMaxInterval"/>, and resets to this floor as soon as
    /// any tag resolves or the connection reconnects.
    /// </summary>
    public TimeSpan TagResolutionRetryInterval { get; set; } = TimeSpan.FromSeconds(10);

    /// <summary>
    /// Ceiling for the exponential tag-resolution retry backoff. A dead device with
    /// thousands of unresolved tags would otherwise probe forever at full width every
    /// <see cref="TagResolutionRetryInterval"/>.
    /// </summary>
    public TimeSpan TagResolutionRetryMaxInterval { get; set; } = TimeSpan.FromMinutes(5);

    /// <summary>
    /// Number of tags per adapter subscribe round trip on the batch subscribe path
    /// (initial subscribe, reconnect re-subscribe, tag-resolution probes).
    /// </summary>
    public int SubscribeBatchSize { get; set; } = 500;

    /// <summary>
    /// Delay inserted BETWEEN reconnect re-subscribe chunks so a site-wide reconnect
    /// paces the device instead of issuing one task per tag in a tight loop. Not applied
    /// on the instance-driven subscribe path — the Deployment Manager already staggers
    /// instance startup there, and double-staggering would only slow failover.
    /// </summary>
    public TimeSpan SubscribeBatchDelay { get; set; } = TimeSpan.FromMilliseconds(50);

    /// <summary>
    /// Number of tags per seed-read chunk. Moderate on purpose: chunking plus the
    /// per-chunk <see cref="SeedReadTimeout"/> answers the "some gateways time out on a
    /// large batch" caveat that originally motivated per-tag seed reads.
    /// </summary>
    public int SeedReadBatchSize { get; set; } = 250;

    /// <summary>Maximum seed-read chunks in flight concurrently.</summary>
    public int SeedReadMaxParallelism { get; set; } = 4;

    /// <summary>
    /// Wall-clock deadline for the WHOLE seed (all chunks and all retry rounds). On
    /// expiry the remaining tags are logged and stay Uncertain until a change
    /// notification arrives — the same outcome as exhausting
    /// <see cref="SeedReadMaxAttempts"/>.
    /// </summary>
    public TimeSpan SeedOverallTimeout { get; set; } = TimeSpan.FromSeconds(120);

    /// <summary>
    /// Coalescing window for pushing tag-quality counters to the health collector.
    /// Counter math stays per message; only the collector push is deferred, and only
    /// when a genuine quality transition occurred. Health reports poll at 30s, so a
    /// short window loses nothing. Disconnect / unsubscribe / reconnect-reset flushes
    /// stay synchronous.
    /// </summary>
    public TimeSpan QualityFlushInterval { get; set; } = TimeSpan.FromSeconds(1);

    /// <summary>
    /// Maximum in-flight supervisory advise commands when the MxGateway adapter has no
    /// write-user context. The gateway worker has no BULK supervisory advise, so the
    /// client pipelines per-item advises after one bulk AddItem instead of issuing them
    /// serially.
    /// </summary>
    public int MxSupervisoryAdviseParallelism { get; set; } = 16;

    /// <summary>Timeout for synchronous write operations to devices.</summary>
    public TimeSpan WriteTimeout { get; set; } = TimeSpan.FromSeconds(30);

    /// <summary>
    /// Per-tag timeout applied to each <see cref="IDataConnection.ReadAsync"/>
    /// call on the initial-subscribe seed path (and reconnect re-seed). A hung device read would
    /// otherwise delay <c>SubscribeCompleted</c> and its acknowledgement indefinitely. On timeout
    /// the tag is treated the same as any other failed seed read — it stays in the pending set and
    /// is retried up to <see cref="SeedReadMaxAttempts"/>, then left Uncertain until a change
    /// notification arrives.
    /// </summary>
    public TimeSpan SeedReadTimeout { get; set; } = TimeSpan.FromSeconds(30);

    /// <summary>
    /// Minimum time a connection must stay up before it is considered stable.
    /// If a connection drops before this threshold, it counts as an unstable
    /// disconnect toward the failover retry count.
    /// </summary>
    public TimeSpan StableConnectionThreshold { get; set; } = TimeSpan.FromSeconds(60);

    /// <summary>
    /// Total number of attempts the seed read makes per tag
    /// before giving up. The initial subscribe seed (and the reconnect re-seed) reads
    /// each just-advised tag to capture its current value; on a cold/fresh advise the
    /// read can race the subscription and return an empty/failed result. A STATIC tag
    /// (one that never fires another OnDataChange) would then stay Uncertain forever,
    /// because its bridge value depends entirely on that seed. Re-reading the still-empty
    /// subset a bounded number of times lets the advise warm up. Minimum 1 (1 = single
    /// read, no retry).
    /// </summary>
    public int SeedReadMaxAttempts { get; set; } = 3;

    /// <summary>
    /// Delay between seed-read attempts for the tags that have
    /// not yet returned a usable value. Applied once per round across the whole pending
    /// subset, so the total added latency is bounded to
    /// <c>(SeedReadMaxAttempts - 1) × SeedReadRetryDelay</c> regardless of tag count.
    /// </summary>
    public TimeSpan SeedReadRetryDelay { get; set; } = TimeSpan.FromMilliseconds(250);
}
