package com.jsjdesigns.poker.game

/**
 * What a hand looks like at one instant.
 *
 * The engine is otherwise a black box that runs a whole hand in one call, which
 * gives a UI nothing to render between actions. A snapshot is published after the
 * deal and after every action so the UI can animate, and so hand history, saving,
 * and replay all have one boundary to work against.
 *
 * Immutable by construction: nothing here aliases live [Seat] state.
 */
data class TableSnapshot(
    val handNumber: Int,
    val street: Street,
    val phase: Phase,
    val board: List<Int>,
    val pot: Int,
    val currentBet: Int,
    val minRaiseSize: Int,
    val button: Int,
    val seats: List<SeatSnapshot>,
    /** Seat currently owed an action, or null between streets and at showdown. */
    val toAct: Int?,
    /** Identity of the decision [toAct] is being asked for; null when nobody is. */
    val toActToken: Long?,
    val lastAction: HandEvent?,
) {
    enum class Phase { DEALT, BETTING, STREET_COMPLETE, SHOWDOWN, COMPLETE }

    /**
     * Hides hole cards this viewer is not entitled to see.
     *
     * The engine publishes the whole truth — replay and debugging need it — so
     * masking is an explicit step rather than something the UI is trusted to
     * remember. Cards already revealed at showdown stay visible.
     */
    fun maskedFor(viewer: Int): TableSnapshot = copy(
        seats = seats.map {
            if (it.index == viewer || it.revealed) it else it.copy(hole = null)
        },
    )

    val activeSeats: List<SeatSnapshot> get() = seats.filter { !it.folded }
}

data class SeatSnapshot(
    val index: Int,
    val name: String,
    val stack: Int,
    val committedThisRound: Int,
    val committedThisHand: Int,
    val folded: Boolean,
    val allIn: Boolean,
    /** Null once masked, or before cards are dealt. */
    val hole: List<Int>?,
    /** True once this hand has been turned face up at showdown. */
    val revealed: Boolean,
    val isButton: Boolean,
)
