namespace ZB.MOM.WW.MxGateway.Server.Configuration;

/// <summary>
/// Security hot-path options bound from <c>MxGateway:Security</c>. Groups the API-key verification
/// cache and <c>last_used</c> write-coalescing knobs with the login and API-key
/// rate-limiting knobs. Defaults preserve safe, low-overhead behaviour without requiring
/// operators to configure anything.
/// </summary>
public sealed class SecurityOptions
{
    /// <summary>The configuration sub-section this binds from.</summary>
    public const string SectionName = "MxGateway:Security";

    /// <summary>
    /// Gets the time-to-live, in seconds, of a cached successful API-key verification. A cache hit
    /// within this window skips the per-call SQLite read (and the library verifier's coupled
    /// <c>last_used</c> write). Explicit invalidation on gateway-initiated revoke/rotate is the
    /// primary correctness mechanism; this short TTL is the backstop for out-of-band mutations.
    /// Set to <c>0</c> to disable caching. Default is 15 seconds.
    /// </summary>
    public int ApiKeyVerificationCacheSeconds { get; init; } = 15;

    /// <summary>
    /// Gets the coalescing window, in seconds, for the <c>last_used_utc</c> write. The library
    /// verifier couples the write into every verification; this decorator forwards at most one
    /// <c>MarkUsed</c> per key per window, so a hammered key produces at most one write per window
    /// rather than one per authenticated RPC. Set to <c>0</c> to forward every write. Default is
    /// 60 seconds (at most one write per key per minute).
    /// </summary>
    public int ApiKeyLastUsedCoalesceSeconds { get; init; } = 60;

    /// <summary>
    /// Gets the maximum number of <c>POST /auth/login</c> attempts permitted per remote IP within
    /// <see cref="LoginRateLimitWindowSeconds"/> before requests are rejected with HTTP 429. Default
    /// is 10.
    /// </summary>
    public int LoginRateLimitPermitLimit { get; init; } = 10;

    /// <summary>
    /// Gets the fixed-window length, in seconds, for the <c>POST /auth/login</c> per-IP rate limit.
    /// Default is 60 seconds.
    /// </summary>
    public int LoginRateLimitWindowSeconds { get; init; } = 60;

    /// <summary>
    /// Gets the number of failed API-key verifications, per <c>(transport peer, key id)</c>
    /// partition, within <see cref="ApiKeyFailureWindowSeconds"/> that trips the in-process
    /// short-circuit. Once tripped, the gRPC auth path rejects further attempts from that partition
    /// before the store read, except for the probe admitted every
    /// <see cref="ApiKeyFailureProbeIntervalSeconds"/>; a successful verification resets the
    /// partition. Default is 10.
    /// </summary>
    public int ApiKeyFailureLimit { get; init; } = 10;

    /// <summary>
    /// Gets the sliding-window length, in seconds, over which API-key verification failures are
    /// counted (for both the per-partition and the per-key-id aggregate layer). Default is 60
    /// seconds.
    /// </summary>
    public int ApiKeyFailureWindowSeconds { get; init; } = 60;

    /// <summary>
    /// Gets the number of failed API-key verifications for one key id, counted across <em>all</em>
    /// transport peers within <see cref="ApiKeyFailureWindowSeconds"/>, that puts the key id into
    /// probe mode. This second layer bounds a distributed or source-rotating sprayer that never
    /// trips any single <c>(peer, key id)</c> partition. Set to <c>0</c> to disable the aggregate
    /// layer. Default is 30.
    /// </summary>
    public int ApiKeyFailureAggregateLimit { get; init; } = 30;

    /// <summary>
    /// Gets the minimum interval, in seconds, between probe admissions for an over-limit partition
    /// or key-id aggregate. An over-limit state is a valve rather than a wall: at most one request
    /// per interval is admitted through to the real verifier, so the holder of the correct secret
    /// can always reach the constant-time compare (and reset the state) while an attacker is
    /// spraying. Set to <c>0</c> to block absolutely instead — not recommended, because an
    /// unauthenticated peer can then deny the key to its legitimate holder for the whole window.
    /// Default is 5 seconds.
    /// </summary>
    public int ApiKeyFailureProbeIntervalSeconds { get; init; } = 5;

    /// <summary>
    /// Gets the maximum number of distinct partitions tracked by the API-key failure counter. The
    /// counter is a bounded LRU so a spray of unique tokens cannot grow memory without limit, and —
    /// since only a validly shaped token mints a key-id partition, capped per transport peer —
    /// cannot be flushed to clear an active block either: eviction prefers fully expired windows and
    /// never removes a partition that is currently over its limit, up to a transient overshoot
    /// ceiling of twice this value. Default is 4096.
    /// </summary>
    public int ApiKeyFailureTrackedPeers { get; init; } = 4096;

    /// <summary>
    /// Gets how many days of canonical audit history the gateway keeps. The audit drain sweeps
    /// <c>audit_event</c> once at startup and hourly thereafter, deleting rows older than this
    /// window; without it the table grows without bound in the same SQLite file the
    /// authentication hot path reads. Must be greater than zero — audit retention cannot be
    /// disabled by configuration, only widened. Default is 90 days.
    /// </summary>
    public int AuditRetentionDays { get; init; } = 90;
}
