package com.jsjdesigns.poker.game

/**
 * An immutable snapshot of the decision a human player is being asked to make.
 *
 * [DecisionContext] cannot cross the UI boundary: it holds a live [Seat] whose
 * fields the game coroutine mutates as the hand proceeds, so anything the UI read
 * from it would be a data race and would not recompose. This copies out only what
 * is needed to render an action bar.
 */
data class DecisionOffer(
    /**
     * Identifies this specific decision.
     *
     * Submissions carry it back so a stale or double tap cannot be applied to a
     * later decision — a second tap landing after the turn moved on would
     * otherwise act on the next street, or even the next hand.
     */
    val token: Long,
    val handNumber: Int,
    val street: Street,
    val seat: Int,
    val hole: List<Int>,
    val board: List<Int>,
    val pot: Int,
    val toCall: Int,
    val minRaiseTo: Int,
    val maxRaiseTo: Int,
    val stack: Int,
    val canCheck: Boolean,
    val canRaise: Boolean,
    /** Public table state needed by deterministic post-action analysis. */
    val activeOpponents: Int,
    val seatsActingAfter: Int,
    val bigBlind: Int,
    /** Current pot this seat can contest if it calls; excludes the call and higher side pots. */
    val eligiblePot: Int,
    /** Highest amount committed by any seat in the current betting round. */
    val currentBet: Int,
) {
    /**
     * What calling actually costs.
     *
     * [toCall] is what is owed; a short stack commits only what it has, because
     * the engine clamps the commitment. This is the single source of truth — the
     * label shown and the action submitted must both come from here, or they
     * drift apart and the UI starts promising prices the player cannot pay.
     */
    val callAmount: Int get() = minOf(toCall, stack)

    /** True when calling would put this player all in. */
    val callIsAllIn: Boolean get() = toCall >= stack

    val inPosition: Boolean get() = seatsActingAfter == 0
}

fun DecisionContext.toOffer(token: Long): DecisionOffer = DecisionOffer(
    token = token,
    handNumber = handNumber,
    street = street,
    seat = seat.index,
    hole = hole.toList(),
    board = board.toList(),
    pot = pot,
    toCall = toCall,
    minRaiseTo = minRaiseTo,
    maxRaiseTo = maxRaiseTo,
    stack = stack,
    canCheck = canCheck,
    canRaise = canRaise,
    activeOpponents = activeOpponents,
    seatsActingAfter = seatsActingAfter,
    bigBlind = bigBlind,
    eligiblePot = eligiblePot,
    currentBet = currentBet,
)
