import Foundation
import PokerCore

/// The chips in front of the player, and what they are allowed to build with them.
///
/// Betting is done by picking chips up rather than dragging a slider: click the hundred twice and
/// the fifty once and you have bet 250. It is slower than a slider by a click or two and far more
/// deliberate, which is the point — a bet you had to count out is a bet you thought about.
public struct ChipTray: Sendable, Equatable {
    /// The largest legal bet: everything the player has.
    public let maximum: Int
    /// The smallest legal raise. Chips cannot always hit it exactly, so anything at or above it
    /// counts — a stack of chips that overshoots a strange minimum is still a legal bet.
    public let minimum: Int
    /// The denominations worth showing. A chip larger than the player's stack is a button that can
    /// never be pressed.
    public let offered: [Chip]

    public init(minimum: Int, maximum: Int) {
        self.minimum = minimum
        self.maximum = maximum
        self.offered = Chip.allCases.filter { $0.value <= maximum }
    }

    public init?(legal: LegalActions) {
        guard legal.canBetOrRaise, legal.maxRaiseTo > 0 else { return nil }
        self.init(minimum: legal.minRaiseTo, maximum: legal.maxRaiseTo)
    }

    /// Whether there is room in the stack for one more of these.
    public func canAdd(_ chip: Chip, to amount: Int) -> Bool {
        amount + chip.value <= maximum
    }

    public func adding(_ chip: Chip, to amount: Int) -> Int {
        canAdd(chip, to: amount) ? amount + chip.value : amount
    }

    /// A bet is good to go once it is worth at least the minimum raise and no more than the stack.
    public func isLegal(_ amount: Int) -> Bool {
        amount >= minimum && amount <= maximum
    }

    /// What is still needed before the bet can be made, or nil once it is legal.
    public func shortfall(_ amount: Int) -> Int? {
        amount >= minimum ? nil : minimum - amount
    }

    /// The fewest chips that make the minimum legal raise, for the button that counts it out for
    /// you. Greedy from the largest chip down, then topped up — chip values divide into each other,
    /// so greedy is optimal here.
    public func quickest(_ target: Int) -> [Chip] {
        var remaining = min(max(target, 0), maximum)
        var out: [Chip] = []
        for chip in offered.sorted(by: >) {
            while remaining >= chip.value {
                out.append(chip)
                remaining -= chip.value
            }
        }
        // Chip values cannot always hit an odd minimum exactly, so round up with one more of the
        // smallest chip rather than leaving the player one short of a legal bet.
        if remaining > 0, let smallest = offered.first, total(out) + smallest.value <= maximum {
            out.append(smallest)
        }
        return out
    }

    public func total(_ chips: [Chip]) -> Int {
        chips.reduce(0) { $0 + $1.value }
    }
}
