package wang.yaojia.webterm.wire

/**
 * Server -> client WS frames (FROZEN contract, plan §3.1). The Android analogue of
 * `src/types.ts:109-120` `ServerMessage` and iOS `WireProtocol.ServerMessage`.
 *
 * Every frame is UNTRUSTED input: A4's `MessageCodec.decodeServer` maps a raw text frame to
 * one of these or to `null` (drop the frame) — an undecodable frame is counted and dropped,
 * NEVER a crash (`src/protocol.ts` resilience). This file only freezes the SHAPES.
 */
public sealed interface ServerMessage {
    /**
     * Attach confirmation. ALWAYS adopt the server-issued id — attaching with an unknown UUID
     * yields a fresh session id (`src/session/manager.ts`). [sessionId] is the raw string here;
     * A4's decode enforces the UUID-v4 shape before constructing this.
     */
    public data class Attached(val sessionId: String) : ServerMessage

    /** Opaque ANSI/UTF-8 bytes; ring-buffer replay and the live stream share this shape. */
    public data class Output(val data: String) : ServerMessage

    /**
     * Shell exit (terminal). `code == WireConstants.SPAWN_FAILED_EXIT_CODE` (-1) means the
     * spawn never succeeded (M4) and [reason] is required server-side in that case.
     */
    public data class Exit(val code: Int, val reason: String? = null) : ServerMessage

    /**
     * Claude Code activity derived from hooks (H2/H3/B4). [pending] = a tool approval is held
     * server-side and the client may approve/reject; [gate] says which kind. An absent
     * `pending` decodes as false; an unrecognized `gate` decodes as null (tolerated as absent
     * so the pending signal is never lost).
     */
    public data class Status(
        val status: ClaudeStatus,
        val detail: String? = null,
        val pending: Boolean = false,
        val gate: GateKind? = null,
    ) : ServerMessage

    /** Latest statusLine telemetry broadcast (B2). */
    public data class Telemetry(val telemetry: StatusTelemetry) : ServerMessage
}

/**
 * Claude Code activity (`src/types.ts:97` `ClaudeStatus`). `unknown` = no hook signal yet;
 * `stuck` (A5) = output silent past STUCK_TTL while not idle/exited.
 */
public enum class ClaudeStatus(public val wire: String) {
    WORKING("working"),
    WAITING("waiting"),
    IDLE("idle"),
    UNKNOWN("unknown"),
    STUCK("stuck");

    public companion object {
        public fun fromWire(value: String): ClaudeStatus? = entries.firstOrNull { it.wire == value }
    }
}

/**
 * Which held approval a `status` carries (`src/types.ts:101` `PermissionGate`). `plan` = an
 * ExitPlanMode three-way gate; `tool` = an ordinary tool gate.
 */
public enum class GateKind(public val wire: String) {
    TOOL("tool"),
    PLAN("plan");

    public companion object {
        public fun fromWire(value: String): GateKind? = entries.firstOrNull { it.wire == value }
    }
}

/** The `type` discriminator whitelist for server frames (`src/types.ts:109-120`). */
public enum class ServerMessageType(public val wire: String) {
    ATTACHED("attached"),
    OUTPUT("output"),
    EXIT("exit"),
    STATUS("status"),
    TELEMETRY("telemetry");

    public companion object {
        public fun fromWire(value: String): ServerMessageType? = entries.firstOrNull { it.wire == value }
    }
}
