package wang.yaojia.webterm.session

import wang.yaojia.webterm.wire.ApproveMode
import wang.yaojia.webterm.wire.ClientMessage
import wang.yaojia.webterm.wire.GateKind

/**
 * One held permission gate as the UI should render it (A6; ports iOS
 * `SessionCore.GateState`, plan §3.2 — frozen shape). Produced by [GateTracker]
 * from `status` frames; a `null` gate means "gate lifted".
 *
 * [epoch] is the stale-decision guard (mirrors the web client's
 * `pendingEpochValue`, public/terminal-session.ts:98-102): it increments only on
 * a pending false→true rising edge, and every approve/reject carries the epoch it
 * was tapped against — a stale epoch is dropped so a slow tap can never approve
 * the NEXT gate (防误批新 gate).
 */
public data class GateState(
    /** `plan` → three-way affordances; `tool` → two-way (public/tabs.ts:334-350). */
    val kind: GateKind,
    /** Server-supplied context (tool name for tool gates); untrusted display text. */
    val detail: String?,
    /** Rising-edge counter; see type doc. */
    val epoch: Int,
) {
    /** The action set for this gate: plan → three-way, tool → two-way. */
    public val affordances: List<Affordance>
        get() = when (kind) {
            GateKind.PLAN -> listOf(Affordance.APPROVE_AUTO, Affordance.APPROVE_REVIEW, Affordance.KEEP_PLANNING)
            GateKind.TOOL -> listOf(Affordance.APPROVE, Affordance.REJECT)
        }

    /**
     * One user-facing gate action. Pure affordance DATA — display strings live in
     * the App layer; the wire mapping mirrors public/tabs.ts:334-350.
     */
    public enum class Affordance {
        /** Plan gate: "Approve + Auto" → `approve(mode = ACCEPT_EDITS)`. */
        APPROVE_AUTO,
        /** Plan gate: "Approve + Review" → `approve(mode = DEFAULT)`. */
        APPROVE_REVIEW,
        /** Plan gate: "Keep Planning" → `reject` (stays in plan mode). */
        KEEP_PLANNING,
        /** Tool gate: "Approve" → `approve(mode = null)`. */
        APPROVE,
        /** Tool gate: "Reject" → `reject`. */
        REJECT;

        /**
         * The exact wire message the web client sends for this affordance
         * (public/tabs.ts:345-347: the plan three-way only ever sends
         * acceptEdits / default / reject — never raw `auto`, plan §3.1 note).
         */
        public val clientMessage: ClientMessage
            get() = when (this) {
                APPROVE_AUTO -> ClientMessage.Approve(ApproveMode.ACCEPT_EDITS)
                APPROVE_REVIEW -> ClientMessage.Approve(ApproveMode.DEFAULT)
                KEEP_PLANNING -> ClientMessage.Reject
                APPROVE -> ClientMessage.Approve(null)
                REJECT -> ClientMessage.Reject
            }
    }
}
