package wang.yaojia.webterm.session

import wang.yaojia.webterm.wire.TimelineEvent

/**
 * The server's `TimelineClass` vocabulary (src/types.ts:429, produced by
 * src/session/timeline.ts CLASS_MAP). Kept in lockstep with
 * [TimelineEvent.KNOWN_CLASSES] (a sync test asserts equality). Internal — the UI
 * sees counts, not class strings.
 */
private object TimelineClassName {
    const val TOOL = "tool"
    const val WAITING = "waiting"
    const val DONE = "done"
    const val STUCK = "stuck"
}

/**
 * "What happened while I was away" summary (A6; ports iOS `SessionCore.AwayDigest`,
 * plan §3.2 — frozen shape). Reduced ONCE after a reconnect from the session's
 * activity timeline (`GET /live-sessions/:id/events`, src/types.ts:434-439) and
 * rendered above the terminal (A21/A22).
 */
public data class AwayDigest(
    /** Events with class `tool` at/after `since` (PreToolUse + PostToolUse). */
    val toolRuns: Int,
    /** Events with class `waiting` (approval requests / notifications). */
    val waitingCount: Int,
    /** Any `done` event seen (Stop / SessionEnd). */
    val sawDone: Boolean,
    /** Any `stuck` event seen (A5 silent-too-long derivation). */
    val sawStuck: Boolean,
    /** The newest events (post-`since`), oldest→newest, truncated to `limit`. */
    val recent: List<TimelineEvent>,
) {
    /** True when there is nothing to show (the UI's "don't render" signal). */
    public val isEmpty: Boolean get() = this == EMPTY

    public companion object {
        /** The all-zero digest: nothing happened → UI skips rendering entirely. */
        public val EMPTY: AwayDigest = AwayDigest(
            toolRuns = 0,
            waitingCount = 0,
            sawDone = false,
            sawStuck = false,
            recent = emptyList(),
        )

        /**
         * Folds a timeline into a digest. PURE function — no I/O, no clock.
         *
         * - [events]: `/events` entries (untrusted server input; order not assumed
         *   — entries are stably re-ordered by `at` before truncation).
         * - [sinceMs]: the moment the user left (ms since epoch); strictly-earlier
         *   events are excluded, an event AT the instant still counts.
         * - [limit]: max [recent] entries kept (newest win); `<= 0` keeps none.
         */
        public fun reduce(events: List<TimelineEvent>, sinceMs: Long, limit: Int): AwayDigest {
            val away = events
                .filter { it.at >= sinceMs }
                .sortedBy { it.at }
            if (away.isEmpty()) return EMPTY

            val keep = if (limit <= 0) 0 else limit
            return AwayDigest(
                toolRuns = away.count { it.eventClass == TimelineClassName.TOOL },
                waitingCount = away.count { it.eventClass == TimelineClassName.WAITING },
                sawDone = away.any { it.eventClass == TimelineClassName.DONE },
                sawStuck = away.any { it.eventClass == TimelineClassName.STUCK },
                recent = away.takeLast(keep),
            )
        }
    }
}
