package wang.yaojia.webterm.wire

/**
 * Boundary validation — the Android analogue of the server's `parseClientMessage` guards
 * (`src/protocol.ts`) and iOS `WireProtocol.Validation`. Pure functions, NEVER throw.
 *
 * These are the primitives A4's [MessageCodec] uses at the trust boundary; they are also
 * exposed so a caller can validate BEFORE sending (the server SILENTLY DISCARDS invalid
 * frames — no error reply comes back, `src/protocol.ts:45-86`).
 */
public object Validation {
    /**
     * Byte-identical port of the server's `SESSION_ID_RE`
     * (`src/protocol.ts:22-23`, exported there; housed here for the Android build):
     * `^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$` case-insensitive.
     *
     * UUID v4 — 8-4-4-4-12 hex groups, version nibble `4`, variant nibble `8/9/a/b`, `/i`
     * (M7). `crypto.randomUUID()` values pass; strings like `"abc123"` are rejected.
     */
    public val SESSION_ID_RE: Regex =
        Regex(
            "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
            RegexOption.IGNORE_CASE,
        )

    /** True when [candidate] is a valid UUID-v4 session id (whole-string match, M7). */
    public fun isValidSessionId(candidate: String): Boolean = SESSION_ID_RE.matches(candidate)

    /**
     * Mirrors the server's `isValidDimension` range check for `resize`: both cols and rows
     * must fall in [WireConstants.RESIZE_RANGE] (1..1000). Frames outside the range are
     * silently dropped by the server — validate before sending (`src/protocol.ts:113-115`).
     */
    public fun isValidResize(cols: Int, rows: Int): Boolean =
        cols in WireConstants.RESIZE_RANGE && rows in WireConstants.RESIZE_RANGE

    /**
     * Mirrors the server's `attach.cwd` check: must be an absolute path (leading `/`). Deeper
     * normalisation stays server-side (`src/protocol.ts:142-149`, Sec L2).
     */
    public fun isAbsoluteCwd(candidate: String): Boolean = candidate.startsWith("/")

    /**
     * Client-frame `type` whitelist (the server's `ALLOWED_TYPES`, `src/protocol.ts:27`).
     * Delegates to the frozen [ClientMessageType] enum so the whitelist has ONE source.
     */
    public fun isAllowedClientType(type: String): Boolean = ClientMessageType.fromWire(type) != null

    /** Server-frame `type` whitelist (`src/types.ts:109-120`), the [ServerMessageType] enum. */
    public fun isAllowedServerType(type: String): Boolean = ServerMessageType.fromWire(type) != null
}
