package wang.yaojia.webterm.wire

import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

/**
 * Client-side tuning constants (FROZEN contract, plan §3.2.1). The Android analogue of iOS
 * `WireProtocol.Tunables`. Downstream modules (`:session-core` PingScheduler/AwayDigest/
 * TitleSanitizer, `:transport-okhttp`, `:api-client`) read these — adding/changing one is a
 * coordination point. All named; no magic numbers anywhere downstream.
 */
public object Tunables {
    /**
     * WS keep-alive ping period. OkHttp's `pingInterval` could drive this, but the miss policy
     * ([PONG_MISS_LIMIT]) is driven by a `PingScheduler` on an injected clock so a half-dead
     * connection ("looks connected but dead") is detected deterministically in tests.
     */
    public val PING_INTERVAL: Duration = 25.seconds

    /**
     * Consecutive missed pongs after which the connection is declared dead: 1 miss is
     * tolerated, the 2nd is a disconnect signal. Any answered ping resets the counter.
     */
    public const val PONG_MISS_LIMIT: Int = 2

    /** Foreground `/live-sessions` polling period (mirrors `public/launcher.ts` REFRESH_MS). */
    public val LIST_POLL_INTERVAL: Duration = 5.seconds

    /**
     * Telemetry chips grey out when [StatusTelemetry.at] is older than this. Mirrors the server
     * default STATUSLINE_TTL_MS (`src/config.ts`); the server value is env-overridable at
     * runtime while the client bakes the default — accepted drift (plan §3.2.1).
     */
    public const val TELEMETRY_STALE_TTL_MS: Long = 30_000L

    /** Away-digest banner auto-fade delay. */
    public val DIGEST_FADE_DELAY: Duration = 8.seconds

    /**
     * Max session-title length after sanitisation; OSC titles are host/attacker-controlled
     * input (consumed by `:session-core` TitleSanitizer, A6).
     */
    public const val TITLE_MAX_LENGTH: Int = 256

    /**
     * Overall pairing-probe deadline: if either probe step hangs past this the probe resolves
     * `timeout` (`:api-client` pairing, A9).
     */
    public val PAIRING_PROBE_TIMEOUT: Duration = 10.seconds

    /**
     * Client WS frame cap. The default (1 MiB on most stacks) is too small: ring-buffer replay
     * arrives as ONE full-snapshot frame and JSON escaping inflates control bytes 1-6x, so the
     * worst case is ~6 x SCROLLBACK_BYTES (default 2 MiB) plus envelope -> 16 MiB.
     *
     * COUPLING WARNING: SCROLLBACK_BYTES is a server env knob the client cannot discover at
     * runtime. If the host raises it past ~2.7 MB (or replay is extremely escape-dense), the
     * receive fails and MUST be classified as the non-retryable `replayTooLarge` failure —
     * never fed into the backoff reconnect loop (plan §3.2 / §9 R6).
     */
    public const val MAX_WS_MESSAGE_BYTES: Long = 16L * 1024 * 1024
}
