package wang.yaojia.webterm.api.models

import kotlinx.serialization.Serializable
import wang.yaojia.webterm.wire.ClaudeStatus
import wang.yaojia.webterm.wire.StatusTelemetry
import java.util.UUID

/**
 * One running (or just-exited) server session from `GET /live-sessions` (read-only discovery — NO
 * Origin header). Mirrors `src/types.ts` `LiveSessionInfo` and iOS `APIClient.LiveSessionInfo`
 * field-for-field.
 *
 * Tolerant decode at the untrusted boundary (plan §4):
 *  - identity/geometry ([id]/[createdAt]/[clientCount]/[exited]/[cols]/[rows]) are REQUIRED — an
 *    entry missing them is dropped by `LossyDecode.listOrNull`;
 *  - an unrecognized `status` string maps to [ClaudeStatus.UNKNOWN] (a future server status must
 *    not make a running session invisible);
 *  - absent `cwd`/`telemetry`/`lastOutputAt` degrade to `null`.
 *
 * NOTE: ms timestamps ([createdAt]/[lastOutputAt]) are `Long` — epoch-ms overflows a 32-bit `Int`
 * (Swift's `Int` is 64-bit, so iOS used `Int`).
 */
@Serializable
public data class LiveSessionInfo(
    @Serializable(with = UuidSerializer::class) val id: UUID,
    /** Server `Date.now()` at spawn (ms since epoch). */
    val createdAt: Long,
    /** Devices currently attached (JOIN/mirror semantics). */
    val clientCount: Int,
    @Serializable(with = ClaudeStatusSerializer::class) val status: ClaudeStatus = ClaudeStatus.UNKNOWN,
    val exited: Boolean,
    val cwd: String? = null,
    /** Tab title / derived label (e.g. 'claude', 'shell'), OSC-title-derived server-side (`src/types.ts`
     *  `title?`). HOST/ATTACKER-influenced — run through `TitleSanitizer` before any UI/list use (§8).
     *  Additive optional field; absent → `null`. */
    val title: String? = null,
    /** Current PTY size (latest-writer-wins on the server). */
    val cols: Int,
    val rows: Int,
    /** Latest statusLine telemetry, if any (B2). */
    val telemetry: StatusTelemetry? = null,
    /** Server ms timestamp of the last PTY output (== createdAt until first output). Additive
     *  optional field — pre-P1 servers omit it; `null` means "no unread data source". */
    val lastOutputAt: Long? = null,
)
