import Foundation

/// Deterministic random source (SplitMix64). Every hand records its seed, so any hand
/// the user played can be replayed card-for-card by the history browser and by tests.
public struct SeededRandom: RandomNumberGenerator, Sendable {
    private var state: UInt64
    public let seed: UInt64

    public init(seed: UInt64) {
        self.seed = seed
        self.state = seed &+ 0x9E3779B97F4A7C15
    }

    public mutating func next() -> UInt64 {
        state = state &+ 0x9E3779B97F4A7C15
        var z = state
        z = (z ^ (z >> 30)) &* 0xBF58476D1CE4E5B9
        z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
        return z ^ (z >> 31)
    }
}

/// A shuffled 52-card deck that deals from the top.
public struct Deck: Sendable {
    private var cards: [Card]
    private var position: Int = 0

    public init(rng: inout SeededRandom) {
        cards = Card.all
        // Fisher-Yates against our own generator so the order depends only on the seed.
        for i in stride(from: cards.count - 1, to: 0, by: -1) {
            let j = Int(rng.next() % UInt64(i + 1))
            cards.swapAt(i, j)
        }
    }

    /// A deck with a fixed order, for tests that need exact cards.
    public init(stacked: [Card]) {
        let remaining = Card.all.filter { c in !stacked.contains(c) }
        cards = stacked + remaining
    }

    public var remaining: Int { cards.count - position }

    public mutating func deal() -> Card {
        precondition(position < cards.count, "deck exhausted")
        defer { position += 1 }
        return cards[position]
    }

    public mutating func deal(_ count: Int) -> [Card] {
        (0..<count).map { _ in deal() }
    }

    /// Discards a card face down, as a real dealer does before each community street.
    public mutating func burn() {
        _ = deal()
    }
}
